PkgTemplates.jl/src/template.jl

127 lines
4.5 KiB
Julia

default_plugins() = [ProjectFile(), SrcDir(), Git(), License(), Readme(), Tests()]
default_user() = LibGit2.getconfig("github.user", "")
default_version() = VersionNumber(VERSION.major)
function default_authors()
name = LibGit2.getconfig("user.name", "")
isempty(name) && return ""
email = LibGit2.getconfig("user.email", "")
return isempty(email) ? name : "$name <$email>"
end
"""
Template(; kwargs...)
A configuration used to generate packages.
## Keyword Arguments
### User Options
- `user::AbstractString="$(default_user())"`: GitHub (or other code hosting service) username.
The default value comes from the global Git config (`github.user`).
If no value is obtained, an `ArgumentError` is thrown.
- `authors::Union{AbstractString, Vector{<:AbstractString}}="$(default_authors())"`: Package authors.
Supply a string for one author or an array for multiple.
Like `user`, it takes its default value from the global Git config (`user.name` and `user.email`).
### Package Options
- `dir::AbstractString="$(contractuser(Pkg.devdir()))"`: Directory to place packages in.
- `host::AbstractString="github.com"`: URL to the code hosting service where packages will reside.
- `julia_version::VersionNumber=$(repr(default_version()))`: Minimum allowed Julia version.
- `develop::Bool=true`: Whether or not to `develop` new packages in the active environment.
### Template Plugins
- `plugins::Vector{<:Plugin}=Plugin[]`: A list of [`Plugin`](@ref)s used by the template.
- `disable_defaults::Vector{DataType}=DataType[]`: Default plugins to disable.
The default plugins are [`ProjectFile`](@ref), [`SrcDir`](@ref), [`Tests`](@ref), [`Readme`](@ref), [`License`](@ref), and [`Git`](@ref).
To override a default plugin instead of disabling it altogether, supply it via `plugins`.
### Interactive Usage
- `interactive::Bool=false`: When set, the template is created interactively, filling unset keywords with user input.
---
To create a package from a `Template`, use the following syntax:
```julia
julia> t = Template();
julia> t("PkgName")
```
"""
struct Template
authors::Vector{String}
dir::String
host::String
julia_version::VersionNumber
plugins::Dict{DataType, <:Plugin}
user::String
end
Template(; interactive::Bool=false, kwargs...) = Template(Val(interactive); kwargs...)
# Non-interactive constructor.
function Template(::Val{false}; kwargs...)
user = getkw(kwargs, :user)
isempty(user) && throw(ArgumentError("No user set, please pass user=username"))
authors = getkw(kwargs, :authors)
authors isa Vector || (authors = map(strip, split(authors, ",")))
dir = abspath(expanduser(getkw(kwargs, :dir)))
host = replace(getkw(kwargs, :host), r".*://" => "")
julia_version = getkw(kwargs, :julia_version)
disabled = getkw(kwargs, :disable_defaults)
enabled = filter(p -> !(typeof(p) in disabled), default_plugins())
append!(enabled, getkw(kwargs, :plugins))
# This comprehension resolves duplicate plugin types by overwriting,
# which means that default plugins get replaced by user values.
plugins = Dict(typeof(p) => p for p in enabled)
return Template(authors, dir, host, julia_version, plugins, user)
end
# Does the template have a plugin that satisfies some predicate?
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.
getkw(kwargs, k) = get(() -> defaultkw(k), kwargs, k)
# Default Template keyword values.
defaultkw(s::Symbol) = defaultkw(Val(s))
defaultkw(::Val{:authors}) = default_authors()
defaultkw(::Val{:dir}) = Pkg.devdir()
defaultkw(::Val{:disable_defaults}) = DataType[]
defaultkw(::Val{:host}) = "github.com"
defaultkw(::Val{:julia_version}) = default_version()
defaultkw(::Val{:plugins}) = Plugin[]
defaultkw(::Val{:user}) = default_user()
"""
(::Template)(pkg::AbstractString)
Generate a package named `pkg` from a [`Template`](@ref).
"""
function (t::Template)(pkg::AbstractString)
endswith(pkg, ".jl") && (pkg = pkg[1:end-3])
pkg_dir = joinpath(t.dir, pkg)
ispath(pkg_dir) && throw(ArgumentError("$pkg_dir already exists"))
mkpath(pkg_dir)
try
foreach((prehook, hook, posthook)) do h
@info "Running $(h)s"
foreach(values(t.plugins)) do p
h(p, t, pkg_dir)
end
end
catch
rm(pkg_dir; recursive=true, force=true)
rethrow()
end
@info "New package is at $pkg_dir"
end