Many misc changes and bug fixes

This commit is contained in:
Chris de Graaf 2019-08-29 23:04:11 +07:00
parent 4fa7d9a62b
commit 810ac5ab28
No known key found for this signature in database
GPG Key ID: 150FFDD9B0073C7B
7 changed files with 152 additions and 112 deletions

View File

@ -2,17 +2,19 @@
environment: environment:
matrix: matrix:
{{#VERSIONS}} {{#VERSIONS}}
- julia_version: {{.}} - julia_version: {{.}}
{{/VERSIONS}} {{/VERSIONS}}
platform: platform:
{{#PLATFORMS}} {{#PLATFORMS}}
- {{.}} - {{.}}
{{/PLATFORMS}} {{/PLATFORMS}}
{{#HAS_NIGHTLY}} {{#HAS_ALLOW_FAILURES}}
matrix: matrix:
allow_failures: allow_failures:
- julia_version: nightly {{#ALLOW_FAILURES}}
{{/HAS_NIGHTLY}} - julia_version: {{.}}
{{/ALLOW_FAILURES}}
{{/HAS_ALLOW_FAILURES}}
branches: branches:
only: only:
- master - master

View File

@ -97,6 +97,7 @@ Perform any work associated with a plugin.
gen_plugin(::Plugin, ::Template, ::AbstractString) = nothing gen_plugin(::Plugin, ::Template, ::AbstractString) = nothing
function gen_plugin(p::BasicPlugin, t::Template, pkg_dir::AbstractString) function gen_plugin(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
source(p) === nothing && return
pkg = basename(pkg_dir) pkg = basename(pkg_dir)
path = joinpath(pkg_dir, destination(p)) path = joinpath(pkg_dir, destination(p))
text = render_plugin(p, t, pkg) text = render_plugin(p, t, pkg)
@ -104,10 +105,8 @@ function gen_plugin(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
end end
function render_plugin(p::BasicPlugin, t::Template, pkg::AbstractString) function render_plugin(p::BasicPlugin, t::Template, pkg::AbstractString)
src = source(p)
src === nothing && return
# TODO template rendering code # TODO template rendering code
return render_file(src, combined_view(p, t, pkg), tags(p)) return render_file(source(p), combined_view(p, t, pkg), tags(p))
end end
function combined_view(p::Plugin, t::Template, pkg::AbstractString) function combined_view(p::Plugin, t::Template, pkg::AbstractString)
@ -123,15 +122,16 @@ Trailing whitespace is removed, and the file will end with a newline.
""" """
function gen_file(file::AbstractString, text::AbstractString) function gen_file(file::AbstractString, text::AbstractString)
mkpath(dirname(file)) mkpath(dirname(file))
text = join(map(rstrip, split(text, "\n")), "\n") text = strip(join(map(rstrip, split(text, "\n")), "\n")) * "\n"
endswith(text , "\n") || (text *= "\n")
write(file, text) write(file, text)
end end
# Render text from a file. # Render text from a file.
render_file(file::AbstractString, view, tags) = render_text(read(file, String), view, tags) function render_file(file::AbstractString, view::Dict{<:AbstractString}, tags)
render_text(read(file, String), view, tags)
end
# Render text, using Mustache's templating system. HTML escaping is disabled. # Render text using Mustache's templating system. HTML escaping is disabled.
function render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=nothing) function render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=nothing)
saved = copy(entityMap) saved = copy(entityMap)
empty!(entityMap) empty!(entityMap)
@ -146,7 +146,7 @@ function render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=no
end end
end end
include(joinpath("plugins", "essentials.jl")) include(joinpath("plugins", "defaults.jl"))
include(joinpath("plugins", "coverage.jl")) include(joinpath("plugins", "coverage.jl"))
include(joinpath("plugins", "ci.jl")) include(joinpath("plugins", "ci.jl"))
include(joinpath("plugins", "citation.jl")) include(joinpath("plugins", "citation.jl"))

View File

@ -1,15 +1,17 @@
const DEFAULT_CI_VERSIONS = ["1.0", "nightly"] # TODO: Update the allowed failures as new versions come out.
const VersionsOrStrings = Vector{Union{VersionNumber, String}} const VersionsOrStrings = Vector{Union{VersionNumber, String}}
const ALLOWED_FAILURES = ["1.3", "nightly"]
const DEFAULT_CI_VERSIONS = VersionsOrStrings([VERSION, "1.0", "nightly"])
format_version(v::VersionNumber) = "$(v.major).$(v.minor)" format_version(v::VersionNumber) = "$(v.major).$(v.minor)"
format_version(v::AbstractString) = string(v)
function collect_versions(versions::Vector, t::Template) function collect_versions(t::Template, versions::Vector)
return unique(sort([versions; format_version(t.julia_version)]; by=string)) vs = [format_version(t.julia_version); map(format_version, versions)]
return unique!(sort!(vs))
end end
abstract type CI <: Plugin end @with_kw struct TravisCI <: BasicPlugin
@with_kw struct TravisCI <: CI
file::String = default_file("travis.yml") file::String = default_file("travis.yml")
linux::Bool = true linux::Bool = true
osx::Bool = true osx::Bool = true
@ -34,9 +36,9 @@ function view(p::TravisCI, t::Template, pkg::AbstractString)
p.osx && push!(os, "osx") p.osx && push!(os, "osx")
p.windows && push!(os, "windows") p.windows && push!(os, "windows")
# TODO: Update the allowed failures as new versions come out.
versions = collect_versions(p.extra_versions, t) versions = collect_versions(t, p.extra_versions)
allow_failures = filter(v -> v in versions, ["1.3", "nightly"]) allow_failures = filter(in(versions), ALLOWED_FAILURES)
x86 = Dict{String, String}[] x86 = Dict{String, String}[]
if p.x86 if p.x86
@ -50,18 +52,20 @@ function view(p::TravisCI, t::Template, pkg::AbstractString)
"ALLOW_FAILURES" => allow_failures, "ALLOW_FAILURES" => allow_failures,
"HAS_ALLOW_FAILURES" => !isempty(allow_failures), "HAS_ALLOW_FAILURES" => !isempty(allow_failures),
"HAS_CODECOV" => hasplugin(t, Codecov), "HAS_CODECOV" => hasplugin(t, Codecov),
"HAS_COVERAGE" => p.coverage && hasplugin(t, Coverage), "HAS_COVERAGE" => p.coverage && hasplugin(t, is_coverage),
"HAS_COVERALLS" => hasplugin(t, Coveralls), "HAS_COVERALLS" => hasplugin(t, Coveralls),
"HAS_DOCUMENTER" => hasplugin(t, Documenter{TravisCI}), "HAS_DOCUMENTER" => hasplugin(t, Documenter{TravisCI}),
"HAS_JOBS" => p.x86 || hasplugin(t, Documenter{TravisCI}), "HAS_JOBS" => p.x86 || hasplugin(t, Documenter{TravisCI}),
"OS" => os, "OS" => os,
"PKG" => pkg, "PKG" => pkg,
"USER" => t.user,
"VERSION" => format_version(t.julia_version), "VERSION" => format_version(t.julia_version),
"VERSIONS" => versions,
"X86" => x86, "X86" => x86,
) )
end end
@with_kw struct AppVeyor <: CI @with_kw struct AppVeyor <: BasicPlugin
file::String = default_file("appveyor.yml") file::String = default_file("appveyor.yml")
x86::Bool = false x86::Bool = false
coverage::Bool = true coverage::Bool = true
@ -77,19 +81,25 @@ badges(::AppVeyor) = Badge(
"https://ci.appveyor.com/project/{{USER}}/{{PKG}}-jl", "https://ci.appveyor.com/project/{{USER}}/{{PKG}}-jl",
) )
function view(p::AppVeyor, t::Template, ::AbstractString) function view(p::AppVeyor, t::Template, pkg::AbstractString)
platforms = ["x64"] platforms = ["x64"]
t.x86 && push!(platforms, "x86") p.x86 && push!(platforms, "x86")
versions = collect_versions(t, p.extra_versions)
allow_failures = filter(in(versions), ALLOWED_FAILURES)
return Dict( return Dict(
"HAS_CODECOV" => t.coverage && hasplugin(t, Codecov), "ALLOW_FAILURES" => allow_failures,
"HAS_NIGHTLY" => "nightly" in versions, "HAS_ALLOW_FAILURES" => !isempty(allow_failures),
"HAS_CODECOV" => p.coverage && hasplugin(t, Codecov),
"PKG" => pkg, "PKG" => pkg,
"PLATFORMS" => os, "PLATFORMS" => platforms,
"VERSIONS" => collect_versions(p.extra_versions, t), "USER" => t.user,
"VERSIONS" => versions,
) )
end end
@with_kw struct CirrusCI <: CI @with_kw struct CirrusCI <: BasicPlugin
file::String = default_file("cirrus.yml") file::String = default_file("cirrus.yml")
image::String = "freebsd-12-0-release-amd64" image::String = "freebsd-12-0-release-amd64"
coverage::Bool = true coverage::Bool = true
@ -109,14 +119,15 @@ function view(p::CirrusCI, t::Template, ::AbstractString)
return Dict( return Dict(
"HAS_CODECOV" => hasplugin(t, Codecov), "HAS_CODECOV" => hasplugin(t, Codecov),
"HAS_COVERALLS" => hasplugin(t, Coveralls), "HAS_COVERALLS" => hasplugin(t, Coveralls),
"HAS_COVERAGE" => p.coverage && hasplugin(t, Coverage), "HAS_COVERAGE" => p.coverage && hasplugin(t, is_coverage),
"IMAGE" => p.image, "IMAGE" => p.image,
"PKG" => pkg, "PKG" => pkg,
"VERSIONS" => collect_versions(p.extra_versions, t), "USER" => t.user,
"VERSIONS" => collect_versions(t, p.extra_versions),
) )
end end
@with_kw struct GitLabCI <: CI @with_kw struct GitLabCI <: BasicPlugin
file::String file::String
documentation::Bool = true documentation::Bool = true
coverage::Bool = true coverage::Bool = true
@ -147,7 +158,11 @@ function view(p::GitLabCI, t::Template, ::AbstractString)
"HAS_COVERAGE" => p.coverage, "HAS_COVERAGE" => p.coverage,
"HAS_DOCUMENTER" => hasplugin(t, Documenter{GitLabCI}), "HAS_DOCUMENTER" => hasplugin(t, Documenter{GitLabCI}),
"PKG" => pkg, "PKG" => pkg,
"USER" => t.user,
"VERSION" => format_version(t.julia_version), "VERSION" => format_version(t.julia_version),
"VERSIONS" => collect_versions(p.extra_versions, t), "VERSIONS" => collect_versions(t, p.extra_versions),
) )
end end
is_ci(::Type) = false
is_ci(::Type{<:Union{AppVeyor, TravisCI, CirrusCI, GitLabCI}}) = true

View File

@ -1,10 +1,6 @@
abstract type Coverage <: Plugin end
const COVERAGE_GITIGNORE = ["*.jl.cov", "*.jl.*.cov", "*.jl.mem"] const COVERAGE_GITIGNORE = ["*.jl.cov", "*.jl.*.cov", "*.jl.mem"]
gitignore(::Coverage) = COVERAGE_GITIGNORE @with_kw struct Codecov <: BasicPlugin
@with_kw struct Codecov <: Coverage
file::Union{String, Nothing} = nothing file::Union{String, Nothing} = nothing
end end
@ -17,7 +13,7 @@ badges(::Codecov) = Badge(
"https://codecov.io/gh/{{USER}}/{{PKG}}.jl", "https://codecov.io/gh/{{USER}}/{{PKG}}.jl",
) )
@with_kw struct Coveralls <: Coverage @with_kw struct Coveralls <: BasicPlugin
file::Union{String, Nothing} = nothing file::Union{String, Nothing} = nothing
end end
@ -29,3 +25,8 @@ badges(::Coveralls) = Badge(
"https://coveralls.io/repos/github/{{USER}}/{{PKG}}.jl/badge.svg?branch=master", "https://coveralls.io/repos/github/{{USER}}/{{PKG}}.jl/badge.svg?branch=master",
"https://coveralls.io/github/{{USER}}/{{PKG}}.jl?branch=master", "https://coveralls.io/github/{{USER}}/{{PKG}}.jl?branch=master",
) )
gitignore(::Union{Codecov, Coveralls}) = COVERAGE_GITIGNORE
is_coverage(::Type) = false
is_coverage(::Type{<:Union{Codecov, Coveralls}}) = true

View File

@ -76,10 +76,16 @@ function gen_plugin(p::License, t::Template, pkg_dir::AbstractString)
gen_file(joinpath(pkg_dir, p.destination), render_plugin(p, t)) gen_file(joinpath(pkg_dir, p.destination), render_plugin(p, t))
end end
struct Gitignore <: Plugin end @with_kw struct Gitignore <: Plugin
ds_store::Bool = true
dev::Bool = true
end
function render_plugin(p::Gitignore, t::Template) function render_plugin(p::Gitignore, t::Template)
entries = mapreduce(gitignore, append!, values(t.plugins); init=[".DS_Store", "/dev/"]) init = String[]
p.ds_store && push!(init, ".DS_Store")
p.dev && push!(init, "/dev/")
entries = mapreduce(gitignore, append!, values(t.plugins); init=init)
# Only ignore manifests at the repo root. # Only ignore manifests at the repo root.
t.manifest || "Manifest.toml" in entries || push!(entries, "/Manifest.toml") t.manifest || "Manifest.toml" in entries || push!(entries, "/Manifest.toml")
unique!(sort!(entries)) unique!(sort!(entries))
@ -110,6 +116,7 @@ function gen_plugin(p::Tests, t::Template, pkg_dir::AbstractString)
open(io -> TOML.print(io, toml), path, "w") open(io -> TOML.print(io, toml), path, "w")
# Generate the manifest. # Generate the manifest.
# This also ensures that keys in Project.toml are sorted properly.
touch(joinpath(pkg_dir, "Manifest.toml")) # File must exist to be modified by Pkg. touch(joinpath(pkg_dir, "Manifest.toml")) # File must exist to be modified by Pkg.
proj = current_project() proj = current_project()
try try

View File

@ -5,16 +5,18 @@ const DOCUMENTER_UUID = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
assets::Vector{<:AbstractString}=String[], assets::Vector{<:AbstractString}=String[],
makedocs_kwargs::Dict{Symbol}=Dict(), makedocs_kwargs::Dict{Symbol}=Dict(),
canonical_url::Union{Function, Nothing}=nothing, canonical_url::Union{Function, Nothing}=nothing,
make_jl::AbstractString="$(contractuser(default_file("make.jl")))",
index_md::AbstractString="$(contractuser(default_file("index.md")))",
) -> Documenter{T} ) -> Documenter{T}
The `Documenter` plugin adds support for documentation generation via [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl). The `Documenter` plugin adds support for documentation generation via [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl).
Documentation deployment depends on `T`, where `T` is some supported CI plugin, or `Nothing` to only support local documentation builds. Documentation deployment depends on `T`, where `T` is some supported CI plugin, or `Nothing` to only support local documentation builds.
## Keyword Arguments ## Keyword Arguments
todo TODO
- `assets::Vector{<:AbstractString}=String[]`: - `assets::Vector{<:AbstractString}=String[]`:
- `makedocs_kwargs::Dict{Symbol}=Dict{Symbol, Any}()`: - `makedocs_kwargs::Dict{Symbol}=Dict{Symbol, Any}()`:
- `canonical_url::Union{Function, Nothing}=nothing`:` - `canonical_url::Union{Function, Nothing}=nothing`:
- `index_md::AbstractString` - `index_md::AbstractString`
- `make_jl::AbstractString` - `make_jl::AbstractString`
@ -29,7 +31,7 @@ struct Documenter{T<:Union{TravisCI, GitLabCI, Nothing}} <: Plugin
make_jl::String make_jl::String
index_md::String index_md::String
# Can't use @kwdef due to some weird precompilation issues. # Can't use @with_kw due to some weird precompilation issues.
function Documenter{T}( function Documenter{T}(
assets::Vector{<:AbstractString}=String[], assets::Vector{<:AbstractString}=String[],
makedocs_kwargs::Dict{Symbol}=Dict{Symbol, Any}(), makedocs_kwargs::Dict{Symbol}=Dict{Symbol, Any}(),
@ -72,6 +74,7 @@ view(p::Documenter, t::Template, pkg::AbstractString) = Dict(
"MAKEDOCS_KWARGS" => map((k, v) -> k => repr(v), collect(p.makedocs_kwargs)), "MAKEDOCS_KWARGS" => map((k, v) -> k => repr(v), collect(p.makedocs_kwargs)),
"PKG" => pkg, "PKG" => pkg,
"REPO" => "https://$(t.host)/$(t.user)/$pkg.jl", "REPO" => "https://$(t.host)/$(t.user)/$pkg.jl",
"USER" => t.user,
) )
function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString) function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString)
@ -80,11 +83,20 @@ function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString)
end end
function gen_plugin(p::Documenter, t::Template, pkg_dir::AbstractString) function gen_plugin(p::Documenter, t::Template, pkg_dir::AbstractString)
# TODO: gen make.jl docs_dir = joinpath(pkg_dir, "docs")
# TODO: gen index.md
# Generate files.
make = render_file(p.make_jl, combined_view(p, t, pkg), tags(p))
gen_file(joinpath(docs_dir, "make.jl"), make)
index = render_file(p.index_md, combined_view(p, t, pkg), tags(p))
gen_file(joinpath(docs_dir, "src", "index.md"), index)
# Copy over any assets.
assets_dir = joinpath(docs_dir, "src", "assets")
isempty(p.assets) || mkpath(assets_dir)
foreach(a -> cp(a, joinpath(assets_dir, basename(a))), p.assets)
# Create the documentation project. # Create the documentation project.
docs_dir = joinpath(pkg_dir, "docs")
proj = current_project() proj = current_project()
try try
Pkg.activate(docs_dir) Pkg.activate(docs_dir)
@ -92,11 +104,6 @@ function gen_plugin(p::Documenter, t::Template, pkg_dir::AbstractString)
finally finally
proj === nothing ? Pkg.activate() : Pkg.activate(proj) proj === nothing ? Pkg.activate() : Pkg.activate(proj)
end end
# Copy any assets.
assets_dir = joinpath(docs_dir, "src", "assets")
isempty(p.assets) || mkpath(assets_dir)
foreach(a -> cp(a, joinpath(assets_dir, basename(asset))), p.assets)
end end
function interactive(::Type{Documenter{T}}) where T function interactive(::Type{Documenter{T}}) where T

View File

@ -1,4 +1,14 @@
const DEFAULT_USER = LibGit2.getconfig("github.user", "")
const DEFAULT_VERSION = VersionNumber(VERSION.major) const DEFAULT_VERSION = VersionNumber(VERSION.major)
const DEFAULT_AUTHORS = let
name = LibGit2.getconfig("user.name", "")
email = LibGit2.getconfig("user.email", "")
if isempty(name)
""
else
isempty(email) ? name : "$name <$email>"
end
end
""" """
Template(; interactive::Bool=false, kwargs...) -> Template Template(; interactive::Bool=false, kwargs...) -> Template
@ -6,52 +16,56 @@ const DEFAULT_VERSION = VersionNumber(VERSION.major)
Records common information used to generate a package. Records common information used to generate a package.
## Keyword Arguments ## Keyword Arguments
- `user::AbstractString=""`: GitHub (or other code hosting service) username.
If left unset, it will take the the global Git config's value (`github.user`). ### User Options
If that is not set, an `ArgumentError` is thrown. - `user::AbstractString="$DEFAULT_USER"`: GitHub (or other code hosting service) username.
This is case-sensitive for some plugins, so take care to enter it correctly! The default value comes from the global Git config (`github.user`).
- `host::AbstractString="github.com"`: URL to the code hosting service where your package will reside. If no value is obtained, an `ArgumentError` is thrown.
Note that while hosts other than GitHub won't cause errors, they are not officially supported and they will cause certain plugins will produce incorrect output. - `authors::Union{AbstractString, Vector{<:AbstractString}}="$DEFAULT_AUTHORS"`: Package authors.
- `authors::Union{AbstractString, Vector{<:AbstractString}}=""`: Names that appear on the license.
Supply a string for one author or an array for multiple. Supply a string for one author or an array for multiple.
Similarly to `user`, it will take the value of of the global Git config's value if it is left unset. Like `user`, it takes its default value from the global Git config (`user.name` and `user.email`).
- `dir::AbstractString=$(contractuser(Pkg.devdir()))`: Directory in which the package will go.
Relative paths are converted to absolute ones at template creation time. ### Package Options
- `julia_version::VersionNumber=$DEFAULT_VERSION`: Minimum allowed Julia version. - `host::AbstractString="github.com"`: URL to the code hosting service where packages will reside.
- `ssh::Bool=false`: Whether or not to use SSH for the git remote. If `false` HTTPS will be used. - `dir::AbstractString="$(contractuser(Pkg.devdir()))"`: Directory to place packages in.
- `julia_version::VersionNumber=$(repr(DEFAULT_VERSION))`: Minimum allowed Julia version.
- `develop::Bool=true`: Whether or not to `develop` new packages in the active environment.
### Git Options
- `git::Bool=true`: Whether or not to create a Git repository for new packages.
- `ssh::Bool=false`: Whether or not to use SSH for the Git remote.
If left unset, HTTPS will be used.
- `manifest::Bool=false`: Whether or not to commit the `Manifest.toml`. - `manifest::Bool=false`: Whether or not to commit the `Manifest.toml`.
- `git::Bool=true`: Whether or not to create a Git repository for generated packages.
- `develop::Bool=true`: Whether or not to `develop` generated packages in the active environment. ### Template Plugins
- `plugins::Vector{<:Plugin}=Plugin[]`: A list of plugins that the package will include. - `plugins::Vector{<:Plugin}=Plugin[]`: A list of [`Plugin`](@ref)s used by the template.
- `disable_default_plugins::Vector{DataType}=DataType[]`: Default plugins to disable. - `disabled_defaults::Vector{DataType}=DataType[]`: Default plugins to disable.
The default plugins are [`Readme`](@ref), [`License`](@ref), [`Tests`](@ref), and [`Gitignore`](@ref). The default plugins are [`Readme`](@ref), [`License`](@ref), [`Tests`](@ref), and [`Gitignore`](@ref).
To override a default plugin instead of disabling it altogether, supply it via `plugins`. To override a default plugin instead of disabling it altogether, supply it via `plugins`.
## Interactive Usage ### Interactive Usage
- `interactive::Bool=false`: When set, creates the template interactively, filling unset keywords with user input. - `interactive::Bool=false`: When set, the template is created interactively, filling unset keywords with user input.
- `fast::Bool=false`: Skips prompts for any unsupplied keywords except `user` and `plugins`. - `fast::Bool=false`: Skips prompts for any unsupplied keywords except `user` and `plugins`, accepting default values.
""" """
struct Template struct Template
user::String
host::String
authors::Vector{String} authors::Vector{String}
dir::String
julia_version::VersionNumber
ssh::Bool
manifest::Bool
git::Bool
develop::Bool develop::Bool
dir::String
git::Bool
host::String
julia_version::VersionNumber
manifest::Bool
plugins::Dict{DataType, <:Plugin} plugins::Dict{DataType, <:Plugin}
ssh::Bool
user::String
end end
Template(; interactive::Bool=false, kwargs...) = make_template(Val(interactive); kwargs...) Template(; interactive::Bool=false, kwargs...) = Template(Val(interactive); kwargs...)
# Non-interactive Template constructor. # Non-interactive constructor.
function make_template(::Val{false}; kwargs...) function Template(::Val{false}; kwargs...)
user = getkw(kwargs, :user) user = getkw(kwargs, :user)
if isempty(user) isempty(user) && throw(ArgumentError("No user set, please pass user=username"))
throw(ArgumentError("No username found, set one with user=username"))
end
host = getkw(kwargs, :host) host = getkw(kwargs, :host)
host = URI(occursin("://", host) ? host : "https://$host").host host = URI(occursin("://", host) ? host : "https://$host").host
@ -63,49 +77,43 @@ function make_template(::Val{false}; kwargs...)
disabled = getkw(kwargs, :disabled_defaults) disabled = getkw(kwargs, :disabled_defaults)
defaults = [Readme, License, Tests, Gitignore] defaults = [Readme, License, Tests, Gitignore]
plugins = map(T -> T(), filter(T -> !in(T, disabled), defaults)) plugins = map(T -> T(), filter(T -> !(T in disabled), defaults))
append!(plugins, getkw(kwargs, :plugins)) append!(plugins, getkw(kwargs, :plugins))
# This comprehensions resolves duplicate plugin types by overwriting, # This comprehension resolves duplicate plugin types by overwriting,
# which means that default plugins get replaced by user values. # which means that default plugins get replaced by user values.
plugin_dict = Dict(typeof(p) => p for p in plugins) plugin_dict = Dict(typeof(p) => p for p in plugins)
return Template( return Template(
user,
host,
authors, authors,
dir,
getkw(kwargs, :julia_version),
getkw(kwargs, :ssh),
getkw(kwargs, :manifest),
getkw(kwargs, :git),
getkw(kwargs, :develop), getkw(kwargs, :develop),
dir,
getkw(kwargs, :git),
host,
getkw(kwargs, :julia_version),
getkw(kwargs, :manifest),
plugin_dict, plugin_dict,
getkw(kwargs, :ssh),
user,
) )
end end
# Does the template have a plugin of this type? Subtypes count too. # Does the template have a plugin that satisfies some predicate?
hasplugin(t::Template, ::Type{T}) where T <: Plugin = any(U -> U <: T, keys(t.plugins)) hasplugin(t::Template, f::Function) = any(f, keys(t.plugins))
hasplugin(t::Template, ::Type{T}) where T <: Plugin = hasplugin(t, U -> U <: T)
# Get a keyword, or compute some default value. # Get a keyword, or compute some default value.
getkw(kwargs, k) = get(() -> defaultkw(k), kwargs, k) getkw(kwargs, k) = get(() -> defaultkw(k), kwargs, k)
# Default Template keyword values. # Default Template keyword values.
defaultkw(s::Symbol) = defaultkw(Val(s)) defaultkw(s::Symbol) = defaultkw(Val(s))
defaultkw(::Val{:user}) = LibGit2.getconfig("github.user", "") defaultkw(::Val{:authors}) = DEFAULT_AUTHORS
defaultkw(::Val{:host}) = "https://github.com"
defaultkw(::Val{:dir}) = Pkg.devdir()
defaultkw(::Val{:julia_version}) = DEFAULT_VERSION
defaultkw(::Val{:ssh}) = false
defaultkw(::Val{:manifest}) = false
defaultkw(::Val{:git}) = true
defaultkw(::Val{:develop}) = true defaultkw(::Val{:develop}) = true
defaultkw(::Val{:plugins}) = Plugin[] defaultkw(::Val{:dir}) = Pkg.devdir()
defaultkw(::Val{:disabled_defaults}) = DataType[] defaultkw(::Val{:disabled_defaults}) = DataType[]
function defaultkw(::Val{:authors}) defaultkw(::Val{:git}) = true
name = LibGit2.getconfig("user.name", "") defaultkw(::Val{:host}) = "github.com"
email = LibGit2.getconfig("user.email", "") defaultkw(::Val{:julia_version}) = DEFAULT_VERSION
isempty(name) && return "" defaultkw(::Val{:manifest}) = false
author = name * " " defaultkw(::Val{:plugins}) = Plugin[]
isempty(email) || (author *= "<$email>") defaultkw(::Val{:ssh}) = false
return [strip(author)] defaultkw(::Val{:user}) = DEFAULT_USER
end