2017-08-11 22:18:09 +00:00
|
|
|
"""
|
|
|
|
Template(; kwargs...) -> Template
|
|
|
|
|
2017-08-21 19:53:00 +00:00
|
|
|
Records common information used to generate a package. If you don't wish to manually
|
2017-08-21 21:19:40 +00:00
|
|
|
create a template, you can use [`interactive_template`](@ref) instead.
|
2017-08-11 22:18:09 +00:00
|
|
|
|
|
|
|
# Keyword Arguments
|
2018-09-26 19:55:33 +00:00
|
|
|
* `user::AbstractString=""`: GitHub (or other code hosting service) username. If left
|
2018-10-22 18:04:26 +00:00
|
|
|
unset, it will take the the global git config's value (`github.user`). If that is not
|
|
|
|
set, an `ArgumentError` is thrown. **This is case-sensitive for some plugins, so take
|
|
|
|
care to enter it correctly.**
|
2017-08-16 22:01:52 +00:00
|
|
|
* `host::AbstractString="github.com"`: URL to the code hosting service where your package
|
2017-08-18 07:08:11 +00:00
|
|
|
will reside. 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.
|
2017-08-23 17:50:52 +00:00
|
|
|
* `license::AbstractString="MIT"`: Name of the package license. If an empty string is
|
|
|
|
given, no license is created. [`available_licenses`](@ref) can be used to list all
|
|
|
|
available licenses, and [`show_license`](@ref) can be used to print out a particular
|
|
|
|
license's text.
|
|
|
|
* `authors::Union{AbstractString, Vector{<:AbstractString}}=""`: Names that appear on the
|
2017-08-28 21:34:34 +00:00
|
|
|
license. Supply a string for one author or an array for multiple. Similarly to `user`,
|
2018-09-26 19:55:33 +00:00
|
|
|
it will take the value of of the global git config's value if it is left unset.
|
2018-10-22 18:04:26 +00:00
|
|
|
* `dir::AbstractString=$(replace(Pkg.devdir(), homedir() => "~"))`: Directory in which the
|
|
|
|
package will go. Relative paths are converted to absolute ones at template creation time.
|
|
|
|
* `julia_version::VersionNumber=$VERSION`: Minimum allowed Julia version.
|
2019-03-21 15:56:43 +00:00
|
|
|
* `ssh::Bool=false`: Whether or not to use SSH for the git remote. If `false` HTTPS will be used.
|
2018-11-07 20:34:28 +00:00
|
|
|
* `manifest::Bool=false`: Whether or not to commit the `Manifest.toml`.
|
2017-08-28 21:34:34 +00:00
|
|
|
* `plugins::Vector{<:Plugin}=Plugin[]`: A list of `Plugin`s that the package will include.
|
2017-08-11 22:18:09 +00:00
|
|
|
"""
|
2018-12-20 19:21:17 +00:00
|
|
|
struct Template
|
|
|
|
user::String
|
|
|
|
host::String
|
|
|
|
license::String
|
|
|
|
authors::String
|
|
|
|
dir::String
|
2017-08-11 22:18:09 +00:00
|
|
|
julia_version::VersionNumber
|
2018-09-24 22:15:52 +00:00
|
|
|
ssh::Bool
|
2018-11-07 20:34:28 +00:00
|
|
|
manifest::Bool
|
2018-12-20 19:21:17 +00:00
|
|
|
plugins::Dict{DataType, <:Plugin}
|
2017-08-11 22:18:09 +00:00
|
|
|
|
2017-08-15 16:10:05 +00:00
|
|
|
function Template(;
|
2017-08-16 22:01:52 +00:00
|
|
|
user::AbstractString="",
|
2017-08-15 16:10:05 +00:00
|
|
|
host::AbstractString="https://github.com",
|
2018-09-19 19:19:16 +00:00
|
|
|
license::AbstractString="MIT",
|
2017-08-23 17:50:52 +00:00
|
|
|
authors::Union{AbstractString, Vector{<:AbstractString}}="",
|
2018-10-22 18:04:26 +00:00
|
|
|
dir::AbstractString=Pkg.devdir(),
|
2017-08-11 22:18:09 +00:00
|
|
|
julia_version::VersionNumber=VERSION,
|
2018-09-24 22:15:52 +00:00
|
|
|
ssh::Bool=false,
|
2018-11-07 20:34:28 +00:00
|
|
|
manifest::Bool=false,
|
2017-08-23 17:50:52 +00:00
|
|
|
plugins::Vector{<:Plugin}=Plugin[],
|
2018-11-05 20:51:11 +00:00
|
|
|
git::Bool=true,
|
2017-08-23 17:50:52 +00:00
|
|
|
)
|
2018-09-26 19:55:33 +00:00
|
|
|
# Check for required Git options for package generation
|
|
|
|
# (you can't commit to a repository without them).
|
2018-11-05 20:51:11 +00:00
|
|
|
git && isempty(LibGit2.getconfig("user.name", "")) && missingopt("user.name")
|
|
|
|
git && isempty(LibGit2.getconfig("user.email", "")) && missingopt("user.email")
|
2018-09-26 19:55:33 +00:00
|
|
|
|
|
|
|
# If no username was set, look for one in the global git config.
|
2018-10-22 19:15:48 +00:00
|
|
|
# Note: This is one of a few GitHub specifics (maybe we could use the host value).
|
2017-08-16 22:01:52 +00:00
|
|
|
if isempty(user)
|
2018-09-26 19:55:33 +00:00
|
|
|
user = LibGit2.getconfig("github.user", "")
|
2017-08-16 22:01:52 +00:00
|
|
|
end
|
|
|
|
if isempty(user)
|
2017-08-15 16:10:05 +00:00
|
|
|
throw(ArgumentError("No GitHub username found, set one with user=username"))
|
|
|
|
end
|
|
|
|
|
|
|
|
host = URI(startswith(host, "https://") ? host : "https://$host").host
|
|
|
|
|
2017-08-23 17:50:52 +00:00
|
|
|
if !isempty(license) && !isfile(joinpath(LICENSE_DIR, license))
|
2017-08-15 16:10:05 +00:00
|
|
|
throw(ArgumentError("License '$license' is not available"))
|
2017-08-11 22:18:09 +00:00
|
|
|
end
|
2017-08-14 20:58:01 +00:00
|
|
|
|
2018-09-26 19:55:33 +00:00
|
|
|
# If no author was set, look for one in the global git config.
|
2017-08-16 22:01:52 +00:00
|
|
|
if isempty(authors)
|
2018-09-26 19:55:33 +00:00
|
|
|
authors = LibGit2.getconfig("user.name", "")
|
2017-08-23 17:50:52 +00:00
|
|
|
elseif isa(authors, Vector)
|
2017-08-11 22:18:09 +00:00
|
|
|
authors = join(authors, ", ")
|
|
|
|
end
|
|
|
|
|
2017-08-25 05:09:49 +00:00
|
|
|
dir = abspath(expanduser(dir))
|
|
|
|
|
2017-08-14 19:15:53 +00:00
|
|
|
plugin_dict = Dict{DataType, Plugin}(typeof(p) => p for p in plugins)
|
|
|
|
if (length(plugins) != length(plugin_dict))
|
2018-09-19 19:19:16 +00:00
|
|
|
@warn "Plugin list contained duplicates, only the last of each type was kept"
|
2017-08-14 19:15:53 +00:00
|
|
|
end
|
2017-08-11 22:18:09 +00:00
|
|
|
|
2018-11-07 20:34:28 +00:00
|
|
|
new(user, host, license, authors, dir, julia_version, ssh, manifest, plugin_dict)
|
2017-08-11 22:18:09 +00:00
|
|
|
end
|
|
|
|
end
|
2017-08-21 19:53:00 +00:00
|
|
|
|
2018-09-19 21:23:11 +00:00
|
|
|
function Base.show(io::IO, t::Template)
|
2018-12-20 20:21:12 +00:00
|
|
|
maybe(s::String) = isempty(s) ? "None" : s
|
2017-12-01 17:33:57 +00:00
|
|
|
spc = " "
|
|
|
|
|
|
|
|
println(io, "Template:")
|
2018-12-20 20:21:12 +00:00
|
|
|
println(io, spc, "→ User: ", maybe(t.user))
|
|
|
|
println(io, spc, "→ Host: ", maybe(t.host))
|
2017-12-07 16:43:40 +00:00
|
|
|
|
2018-12-20 20:21:12 +00:00
|
|
|
print(io, spc, "→ License: ")
|
2017-12-07 16:43:40 +00:00
|
|
|
if isempty(t.license)
|
|
|
|
println(io, "None")
|
|
|
|
else
|
2018-12-20 20:21:12 +00:00
|
|
|
println(io, t.license, " ($(t.authors) ", year(today()), ")")
|
2017-12-01 17:33:57 +00:00
|
|
|
end
|
|
|
|
|
2018-12-20 20:21:12 +00:00
|
|
|
println(io, spc, "→ Package directory: ", replace(maybe(t.dir), homedir() => "~"))
|
|
|
|
println(io, spc, "→ Minimum Julia version: v", version_floor(t.julia_version))
|
|
|
|
println(io, spc, "→ SSH remote: ", t.ssh ? "Yes" : "No")
|
|
|
|
println(io, spc, "→ Commit Manifest.toml: ", t.manifest ? "Yes" : "No")
|
2017-12-01 17:33:57 +00:00
|
|
|
|
2018-12-20 20:21:12 +00:00
|
|
|
print(io, spc, "→ Plugins:")
|
2017-12-01 17:33:57 +00:00
|
|
|
if isempty(t.plugins)
|
2017-12-05 20:03:57 +00:00
|
|
|
print(io, " None")
|
2017-12-01 17:33:57 +00:00
|
|
|
else
|
|
|
|
for plugin in sort(collect(values(t.plugins)); by=string)
|
|
|
|
println(io)
|
|
|
|
buf = IOBuffer()
|
|
|
|
show(buf, plugin)
|
2018-12-20 20:21:12 +00:00
|
|
|
print(io, spc^2, "• ")
|
2017-12-01 17:33:57 +00:00
|
|
|
print(io, join(split(String(take!(buf)), "\n"), "\n$(spc^2)"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-21 19:53:00 +00:00
|
|
|
"""
|
2017-08-25 06:25:26 +00:00
|
|
|
interactive_template(; fast::Bool=false) -> Template
|
2017-08-21 19:53:00 +00:00
|
|
|
|
2017-08-22 18:49:24 +00:00
|
|
|
Interactively create a [`Template`](@ref). If `fast` is set, defaults will be assumed for
|
|
|
|
all values except username and plugins.
|
2017-08-21 19:53:00 +00:00
|
|
|
"""
|
2018-11-05 20:05:57 +00:00
|
|
|
function interactive_template(; git::Bool=true, fast::Bool=false)
|
2018-09-17 20:26:37 +00:00
|
|
|
@info "Default values are shown in [brackets]"
|
2017-08-21 19:53:00 +00:00
|
|
|
# Getting the leaf types in a separate thread eliminates an awkward wait after
|
|
|
|
# "Select plugins" is printed.
|
2018-11-05 22:12:08 +00:00
|
|
|
plugin_types = @async leaves(Plugin)
|
2017-08-21 19:53:00 +00:00
|
|
|
kwargs = Dict{Symbol, Any}()
|
|
|
|
|
2017-08-22 19:31:28 +00:00
|
|
|
default_user = LibGit2.getconfig("github.user", "")
|
2018-12-20 20:21:12 +00:00
|
|
|
print("Username [", isempty(default_user) ? "REQUIRED" : default_user, "]: ")
|
2017-08-21 19:53:00 +00:00
|
|
|
user = readline()
|
|
|
|
kwargs[:user] = if !isempty(user)
|
|
|
|
user
|
|
|
|
elseif !isempty(default_user)
|
|
|
|
default_user
|
|
|
|
else
|
|
|
|
throw(ArgumentError("Username is required"))
|
|
|
|
end
|
|
|
|
|
2018-11-05 20:05:57 +00:00
|
|
|
kwargs[:host] = if fast || !git
|
|
|
|
"https://github.com" # If Git isn't enabled, this value never gets used.
|
2017-08-21 19:53:00 +00:00
|
|
|
else
|
2017-08-22 18:49:24 +00:00
|
|
|
default_host = "github.com"
|
2018-11-07 20:34:28 +00:00
|
|
|
print("Code hosting service [$default_host]: ")
|
2017-08-22 18:49:24 +00:00
|
|
|
host = readline()
|
|
|
|
isempty(host) ? default_host : host
|
2017-08-21 19:53:00 +00:00
|
|
|
end
|
|
|
|
|
2017-08-22 18:49:24 +00:00
|
|
|
kwargs[:license] = if fast
|
|
|
|
"MIT"
|
|
|
|
else
|
2018-11-07 20:34:28 +00:00
|
|
|
println("License:")
|
2017-08-22 18:49:24 +00:00
|
|
|
io = IOBuffer()
|
2017-08-23 17:50:52 +00:00
|
|
|
available_licenses(io)
|
|
|
|
licenses = ["" => "", collect(LICENSES)...]
|
2018-09-19 19:19:16 +00:00
|
|
|
menu = RadioMenu(String["None", split(String(take!(io)), "\n")...])
|
2017-08-23 17:50:52 +00:00
|
|
|
# If the user breaks out of the menu with Ctrl-c, the result is -1, the absolute
|
2017-08-22 18:49:24 +00:00
|
|
|
# value of which correponds to no license.
|
2018-09-26 19:55:33 +00:00
|
|
|
first(licenses[abs(request(menu))])
|
2017-08-22 18:49:24 +00:00
|
|
|
end
|
|
|
|
|
2018-09-26 19:55:33 +00:00
|
|
|
# We don't need to ask for authors if there is no license,
|
2017-08-25 14:18:29 +00:00
|
|
|
# because the license is the only place that they matter.
|
|
|
|
kwargs[:authors] = if fast || isempty(kwargs[:license])
|
2017-08-22 18:49:24 +00:00
|
|
|
LibGit2.getconfig("user.name", "")
|
|
|
|
else
|
|
|
|
default_authors = LibGit2.getconfig("user.name", "")
|
|
|
|
default_str = isempty(default_authors) ? "None" : default_authors
|
2018-11-07 20:34:28 +00:00
|
|
|
print("Package author(s) [$default_str]: ")
|
2017-08-22 18:49:24 +00:00
|
|
|
authors = readline()
|
|
|
|
isempty(authors) ? default_authors : authors
|
|
|
|
end
|
|
|
|
|
|
|
|
kwargs[:dir] = if fast
|
2018-10-22 18:04:26 +00:00
|
|
|
Pkg.devdir()
|
2017-08-22 18:49:24 +00:00
|
|
|
else
|
2018-10-22 18:04:26 +00:00
|
|
|
default_dir = Pkg.devdir()
|
2018-11-07 20:34:28 +00:00
|
|
|
print("Path to package directory [$default_dir]: ")
|
2017-08-22 18:49:24 +00:00
|
|
|
dir = readline()
|
|
|
|
isempty(dir) ? default_dir : dir
|
|
|
|
end
|
|
|
|
|
|
|
|
kwargs[:julia_version] = if fast
|
|
|
|
VERSION
|
|
|
|
else
|
|
|
|
default_julia_version = VERSION
|
2018-12-20 20:21:12 +00:00
|
|
|
print("Minimum Julia version [", version_floor(default_julia_version), "]: ")
|
2017-08-22 18:49:24 +00:00
|
|
|
julia_version = readline()
|
|
|
|
isempty(julia_version) ? default_julia_version : VersionNumber(julia_version)
|
|
|
|
end
|
2017-08-21 19:53:00 +00:00
|
|
|
|
2018-11-05 20:05:57 +00:00
|
|
|
kwargs[:ssh] = if fast || !git
|
2018-09-24 22:15:52 +00:00
|
|
|
false
|
|
|
|
else
|
|
|
|
print("Set remote to SSH? [no]: ")
|
2018-11-07 20:34:28 +00:00
|
|
|
uppercase(readline()) in ["Y", "YES", "T", "TRUE"]
|
2018-09-24 22:15:52 +00:00
|
|
|
end
|
|
|
|
|
2018-11-07 20:34:28 +00:00
|
|
|
kwargs[:manifest] = if fast
|
|
|
|
false
|
|
|
|
else
|
|
|
|
print("Commit Manifest.toml? [no]: ")
|
|
|
|
uppercase(readline()) in ["Y", "YES", "T", "TRUE"]
|
|
|
|
end
|
|
|
|
|
|
|
|
println("Plugins:")
|
2017-08-21 21:19:40 +00:00
|
|
|
# Only include plugin types which have an `interactive` method.
|
2018-09-17 21:43:12 +00:00
|
|
|
plugin_types = filter(t -> hasmethod(interactive, (Type{t},)), fetch(plugin_types))
|
2017-08-21 19:53:00 +00:00
|
|
|
type_names = map(t -> split(string(t), ".")[end], plugin_types)
|
|
|
|
menu = MultiSelectMenu(String.(type_names); pagesize=length(type_names))
|
|
|
|
selected = collect(request(menu))
|
2018-11-05 22:12:08 +00:00
|
|
|
kwargs[:plugins] = Vector{Plugin}(map(interactive, getindex(plugin_types, selected)))
|
2017-08-21 19:53:00 +00:00
|
|
|
|
2018-11-05 20:51:11 +00:00
|
|
|
return Template(; git=git, kwargs...)
|
2017-08-21 19:53:00 +00:00
|
|
|
end
|
|
|
|
|
2018-11-05 22:12:08 +00:00
|
|
|
leaves(T::Type)::Vector{DataType} = isconcretetype(T) ? [T] : vcat(leaves.(subtypes(T))...)
|
2018-10-22 19:15:48 +00:00
|
|
|
|
|
|
|
missingopt(name) = @warn "Git config option '$name' missing, package generation will fail unless you supply a GitConfig"
|