Add prehooks/posthooks for more fine-grained plugin control
This commit is contained in:
parent
12fcc121fa
commit
146c1bdbe5
|
@ -61,9 +61,9 @@ version = "1.1.0"
|
|||
|
||||
[[Parameters]]
|
||||
deps = ["OrderedCollections"]
|
||||
git-tree-sha1 = "1dfd7cd50a8eb06ef693a4c2bbe945943cd000c5"
|
||||
git-tree-sha1 = "b62b2558efb1eef1fa44e4be5ff58a515c287e38"
|
||||
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
|
||||
[[Pkg]]
|
||||
deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
|
||||
|
|
|
@ -11,6 +11,7 @@ Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
|
|||
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
|
||||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[compat]
|
||||
julia = "1"
|
||||
|
|
|
@ -50,7 +50,7 @@ view(p::Documenter, t::Template, pkg::AbstractString) = Dict(
|
|||
"USER" => t.user,
|
||||
)
|
||||
|
||||
function gen_plugin(p::Documenter, t::Template, pkg_dir::AbstractString)
|
||||
function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
|
||||
pkg = basename(pkg_dir)
|
||||
docs_dir = joinpath(pkg_dir, "docs")
|
||||
|
||||
|
@ -91,10 +91,10 @@ Third, we implement [`view`](@ref), which is used to fill placeholders in badges
|
|||
view
|
||||
```
|
||||
|
||||
Finally, we implement [`gen_plugin`](@ref), which is the real workhorse for the plugin.
|
||||
Finally, we implement [`hook`](@ref), which is the real workhorse for the plugin.
|
||||
|
||||
```@docs
|
||||
gen_plugin
|
||||
hook
|
||||
```
|
||||
|
||||
Inside of this function, we call a few more functions, which help us with text templating.
|
||||
|
@ -167,7 +167,7 @@ Finally, we implement [`view`](@ref) to fill in the placeholders that we saw in
|
|||
|
||||
## Doing Extra Work With `BasicPlugin`s
|
||||
|
||||
Notice that we didn't have to implement [`gen_plugin`](@ref) for our plugin.
|
||||
Notice that we didn't have to implement [`hook`](@ref) for our plugin.
|
||||
It's implemented for all [`BasicPlugin`](@ref)s, like so:
|
||||
|
||||
```julia
|
||||
|
@ -175,7 +175,7 @@ function render_plugin(p::BasicPlugin, t::Template, pkg::AbstractString)
|
|||
return render_file(source(p), combined_view(p, t, pkg), tags(p))
|
||||
end
|
||||
|
||||
function gen_plugin(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
|
||||
function hook(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
|
||||
source(p) === nothing && return
|
||||
pkg = basename(pkg_dir)
|
||||
path = joinpath(pkg_dir, destination(p))
|
||||
|
@ -191,7 +191,7 @@ It creates `runtests.jl`, but it also modifies the `Project.toml` to include the
|
|||
|
||||
Of course, we could use a normal [`Plugin`](@ref), but it turns out there's a way to avoid that while still getting the extra capbilities that we want.
|
||||
|
||||
The plugin implements its own `gen_plugin`, but uses `invoke` to avoid duplicating the file creation code:
|
||||
The plugin implements its own `hook`, but uses `invoke` to avoid duplicating the file creation code:
|
||||
|
||||
```julia
|
||||
@with_kw_noshow struct Tests <: BasicPlugin
|
||||
|
@ -202,9 +202,9 @@ source(p::Tests) = p.file
|
|||
destination(::Tests) = joinpath("test", "runtests.jl")
|
||||
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
||||
|
||||
function gen_plugin(p::Tests, t::Template, pkg_dir::AbstractString)
|
||||
function hook(p::Tests, t::Template, pkg_dir::AbstractString)
|
||||
# Do the normal BasicPlugin behaviour to create the test script.
|
||||
invoke(gen_plugin, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
invoke(hook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
# Do some other work.
|
||||
add_test_dependency()
|
||||
end
|
||||
|
|
|
@ -28,10 +28,12 @@ These plugins are included by default.
|
|||
They can be overridden by supplying another value via the `plugins` keyword, or disabled by supplying the type via the `disable_defaults` keyword.
|
||||
|
||||
```@docs
|
||||
Gitignore
|
||||
License
|
||||
Readme
|
||||
ProjectFile
|
||||
SrcDir
|
||||
Tests
|
||||
Readme
|
||||
License
|
||||
Git
|
||||
```
|
||||
|
||||
### Continuous Integration (CI)
|
||||
|
@ -63,6 +65,7 @@ Documenter
|
|||
### Miscellaneous
|
||||
|
||||
```@docs
|
||||
Develop
|
||||
Citation
|
||||
```
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@ using Base.Filesystem: contractuser
|
|||
|
||||
using Dates: month, today, year
|
||||
using InteractiveUtils: subtypes
|
||||
using LibGit2: LibGit2, GitRemote
|
||||
using LibGit2: LibGit2, GitRemote, GitRepo
|
||||
using Pkg: Pkg, TOML, PackageSpec
|
||||
using REPL.TerminalMenus: MultiSelectMenu, RadioMenu, request
|
||||
using UUIDs: uuid4
|
||||
|
||||
using Mustache: render
|
||||
using Parameters: @with_kw_noshow
|
||||
|
@ -19,11 +20,14 @@ export
|
|||
Citation,
|
||||
Codecov,
|
||||
Coveralls,
|
||||
Develop,
|
||||
Documenter,
|
||||
Gitignore,
|
||||
Git,
|
||||
GitLabCI,
|
||||
License,
|
||||
ProjectFile,
|
||||
Readme,
|
||||
SrcDir,
|
||||
Tests,
|
||||
TravisCI
|
||||
|
||||
|
@ -36,7 +40,6 @@ When implementing a new plugin, subtype this type to have full control over its
|
|||
abstract type Plugin end
|
||||
|
||||
include("template.jl")
|
||||
include("generate.jl")
|
||||
include("plugin.jl")
|
||||
include("interactive.jl")
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Return the view to be passed to the text templating engine for this plugin.
|
|||
`pkg` is the name of the package being generated.
|
||||
|
||||
For [`BasicPlugin`](@ref)s, this is used for both the plugin badges (see [`badges`](@ref)) and the template file (see [`source`](@ref)).
|
||||
For other [`Plugin`](@ref)s, it is used only for badges, but you can always call it yourself as part of your [`gen_plugin`](@ref) implementation.
|
||||
For other [`Plugin`](@ref)s, it is used only for badges, but you can always call it yourself as part of your [`hook`](@ref) implementation.
|
||||
|
||||
By default, an empty `Dict` is returned.
|
||||
"""
|
||||
|
@ -124,19 +124,44 @@ function badges(p::Plugin, t::Template, pkg::AbstractString)
|
|||
end
|
||||
|
||||
"""
|
||||
gen_plugin(::Plugin, ::Template, pkg::AbstractString)
|
||||
prehook(::Plugin, ::Template, pkg_dir::AbstractString)
|
||||
|
||||
Do some work associated with a plugin **before** any files are generated.
|
||||
At this point, `pkg_dir` is an empty directory that will eventually contain the package.
|
||||
"""
|
||||
prehook(::Plugin, ::Template, ::AbstractString) = nothing
|
||||
|
||||
function prehook(p::T, ::Template, ::AbstractString) where T <: BasicPlugin
|
||||
src = source(p)
|
||||
src === nothing && return
|
||||
isfile(src) || throw(ArgumentError("$(nameof(T)): The file $src does not exist"))
|
||||
end
|
||||
|
||||
"""
|
||||
posthook(::Plugin, ::Template, pkg_dir::AbstractString)
|
||||
|
||||
Do some work associated with a plugin **after** after files have been generated.
|
||||
"""
|
||||
posthook(::Plugin, ::Template, ::AbstractString) = nothing
|
||||
|
||||
"""
|
||||
hook(::Plugin, ::Template, pkg_dir::AbstractString)
|
||||
|
||||
Perform any work associated with a plugin.
|
||||
`pkg` is the name of the package being generated.
|
||||
`pkg_dir` is the directory in which the package is being generated (so `basename(pkg_dir)` is the package name).
|
||||
|
||||
For [`Plugin`](@ref)s that are not [`BasicPlugin`](@ref)s, this is the only function that really needs to be implemented.
|
||||
If you want your plugin to do anything at all during package generation, you should implement it here.
|
||||
If you want your plugin to do something during the main phase of package generation, you should implement it here.
|
||||
|
||||
You should **not** implement this function for `BasicPlugin`s.
|
||||
See also: [`prehook`](@ref) and [`posthook`](@ref).
|
||||
|
||||
!!! note
|
||||
You usually shouldn't implement this function for [`BasicPlugin`](@ref)s.
|
||||
If you do, it should probably `invoke` the generic method (otherwise, there's no reason to subtype `BasicPlugin`).
|
||||
"""
|
||||
gen_plugin(::Plugin, ::Template, ::AbstractString) = nothing
|
||||
hook(::Plugin, ::Template, ::AbstractString) = nothing
|
||||
|
||||
function gen_plugin(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
|
||||
function hook(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
|
||||
source(p) === nothing && return
|
||||
pkg = basename(pkg_dir)
|
||||
path = joinpath(pkg_dir, destination(p))
|
||||
|
@ -184,4 +209,5 @@ include(joinpath("plugins", "defaults.jl"))
|
|||
include(joinpath("plugins", "coverage.jl"))
|
||||
include(joinpath("plugins", "ci.jl"))
|
||||
include(joinpath("plugins", "citation.jl"))
|
||||
include(joinpath("plugins", "develop.jl"))
|
||||
include(joinpath("plugins", "documenter.jl"))
|
||||
|
|
|
@ -1,18 +1,5 @@
|
|||
const TEST_UUID = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
const TEST_DEP = PackageSpec(; name="Test", uuid=TEST_UUID)
|
||||
const LICENSES = Dict(
|
||||
"MIT" => "MIT \"Expat\" License",
|
||||
"BSD2" => "Simplified \"2-clause\" BSD License",
|
||||
"BSD3" => "Modified \"3-clause\" BSD License",
|
||||
"ISC" => "Internet Systems Consortium License",
|
||||
"ASL" => "Apache License, Version 2.0",
|
||||
"MPL" => "Mozilla Public License, Version 2.0",
|
||||
"GPL-2.0+" => "GNU Public License, Version 2.0+",
|
||||
"GPL-3.0+" => "GNU Public License, Version 3.0+",
|
||||
"LGPL-2.1+" => "Lesser GNU Public License, Version 2.1+",
|
||||
"LGPL-3.0+" => "Lesser GNU Public License, Version 3.0+",
|
||||
"EUPL-1.2+" => "European Union Public Licence, Version 1.2+",
|
||||
)
|
||||
|
||||
badge_order() = [
|
||||
Documenter{GitLabCI},
|
||||
|
@ -25,6 +12,67 @@ badge_order() = [
|
|||
Coveralls,
|
||||
]
|
||||
|
||||
"""
|
||||
ProjectFile()
|
||||
|
||||
Creates a `Project.toml`.
|
||||
"""
|
||||
struct ProjectFile <: Plugin end
|
||||
|
||||
# Create Project.toml in the prehook because other hooks might depend on it.
|
||||
function prehook(::ProjectFile, t::Template, pkg_dir::AbstractString)
|
||||
toml = Dict(
|
||||
"name" => basename(pkg_dir),
|
||||
"uuid" => uuid4(),
|
||||
"authors" => t.authors,
|
||||
"compat" => Dict("julia" => compat_version(t.julia_version)),
|
||||
)
|
||||
open(io -> TOML.print(io, toml), joinpath(pkg_dir, "Project.toml"), "w")
|
||||
end
|
||||
|
||||
"""
|
||||
compat_version(v::VersionNumber) -> String
|
||||
|
||||
Format a `VersionNumber` to exclude trailing zero components.
|
||||
"""
|
||||
function compat_version(v::VersionNumber)
|
||||
return if v.patch == 0 && v.minor == 0
|
||||
"$(v.major)"
|
||||
elseif v.patch == 0
|
||||
"$(v.major).$(v.minor)"
|
||||
else
|
||||
"$(v.major).$(v.minor).$(v.patch)"
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
SrcDir(; file="$(contractuser(default_file("src", "module.jl")))")
|
||||
|
||||
Creates a module entrypoint.
|
||||
"""
|
||||
@with_kw_noshow mutable struct SrcDir <: BasicPlugin
|
||||
file::String = default_file("src", "module.jl")
|
||||
destination::String = joinpath("src", "<module>.jl")
|
||||
end
|
||||
|
||||
# Don't display the destination field.
|
||||
function Base.show(io::IO, ::MIME"text/plain", p::SrcDir)
|
||||
indent = get(io, :indent, 0)
|
||||
print(io, repeat(' ', indent), "SrcDir:")
|
||||
print(io, "\n", repeat(' ', indent + 2), "file: ", show_field(p.file))
|
||||
end
|
||||
|
||||
source(p::SrcDir) = p.file
|
||||
destination(p::SrcDir) = p.destination
|
||||
view(::SrcDir, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
||||
|
||||
# Update the destination now that we know the package name.
|
||||
# Kind of hacky, but oh well.
|
||||
function prehook(p::SrcDir, t::Template, pkg_dir::AbstractString)
|
||||
invoke(prehook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
p.destination = joinpath("src", basename(pkg_dir) * ".jl")
|
||||
end
|
||||
|
||||
"""
|
||||
Readme(;
|
||||
file="$(contractuser(default_file("README.md")))",
|
||||
|
@ -113,32 +161,88 @@ view(::License, t::Template, ::AbstractString) = Dict(
|
|||
)
|
||||
|
||||
"""
|
||||
Gitignore(; ds_store=true, dev=true)
|
||||
Git(; ignore=String[], ssh=false, manifest=false, gpgsign=false)
|
||||
|
||||
Creates a `.gitignore` file.
|
||||
Creates a Git repository and a `.gitignore` file.
|
||||
|
||||
## Keyword Arguments
|
||||
- `ds_store::Bool`: Whether or not to ignore MacOS's `.DS_Store` files.
|
||||
- `dev::Bool`: Whether or not to ignore the directory of locally-developed packages.
|
||||
- `ignore::Vector{<:AbstractString}`: Patterns to add to the `.gitignore`.
|
||||
See also: [`gitignore`](@ref).
|
||||
- `ssh::Bool`: Whether or not to use SSH for the remote.
|
||||
If left unset, HTTPS is used.
|
||||
- `manifest::Bool`: Whether or not to commit `Manifest.toml`.
|
||||
- `gpgsign::Bool`: Whether or not to sign commits with your GPG key.
|
||||
This option requires that the Git CLI is installed.
|
||||
"""
|
||||
@with_kw_noshow struct Gitignore <: Plugin
|
||||
ds_store::Bool = true
|
||||
dev::Bool = true
|
||||
@with_kw_noshow struct Git <: Plugin
|
||||
ignore::Vector{String} = []
|
||||
ssh::Bool = false
|
||||
manifest::Bool = false
|
||||
gpgsign::Bool = false
|
||||
end
|
||||
|
||||
function render_plugin(p::Gitignore, t::Template)
|
||||
init = String[]
|
||||
p.ds_store && push!(init, ".DS_Store")
|
||||
p.dev && push!(init, "/dev/")
|
||||
entries = mapreduce(gitignore, append!, values(t.plugins); init=init)
|
||||
gitignore(p::Git) = p.ignore
|
||||
|
||||
# Set up the Git repository.
|
||||
function prehook(p::Git, t::Template, pkg_dir::AbstractString)
|
||||
if p.gpgsign && try run(pipeline(`git --version`; stdout=devnull)); false catch; true end
|
||||
throw(ArgumentError("Git: gpgsign is set but the Git CLI is not installed"))
|
||||
end
|
||||
LibGit2.with(LibGit2.init(pkg_dir)) do repo
|
||||
commit(p, repo, pkg_dir, "Initial commit")
|
||||
pkg = basename(pkg_dir)
|
||||
url = if p.ssh
|
||||
"git@$(t.host):$(t.user)/$pkg.jl.git"
|
||||
else
|
||||
"https://$(t.host)/$(t.user)/$pkg.jl"
|
||||
end
|
||||
LibGit2.with(GitRemote(repo, "origin", url)) do remote
|
||||
# TODO: `git pull` still requires some Git branch config.
|
||||
LibGit2.add_push!(repo, remote, "refs/heads/master")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Create the .gitignore.
|
||||
function hook(p::Git, t::Template, pkg_dir::AbstractString)
|
||||
gen_file(joinpath(pkg_dir, ".gitignore"), render_plugin(p, t))
|
||||
end
|
||||
|
||||
# Commit the files
|
||||
function posthook(p::Git, t::Template, pkg_dir::AbstractString)
|
||||
# Ensure that the manifest exists if it's going to be committed.
|
||||
manifest = joinpath(pkg_dir, "Manifest.toml")
|
||||
if p.manifest && !isfile(manifest)
|
||||
touch(manifest)
|
||||
with_project(Pkg.update, pkg_dir)
|
||||
end
|
||||
|
||||
LibGit2.with(GitRepo(pkg_dir)) do repo
|
||||
LibGit2.add!(repo, ".")
|
||||
msg = "Files generated by PkgTemplates"
|
||||
installed = Pkg.installed()
|
||||
if haskey(installed, "PkgTemplates")
|
||||
ver = string(installed["PkgTemplates"])
|
||||
msg *= "\n\nPkgTemplates version: $ver"
|
||||
end
|
||||
commit(p, repo, pkg_dir, msg)
|
||||
end
|
||||
end
|
||||
|
||||
function commit(p::Git, repo::GitRepo, pkg_dir::AbstractString, msg::AbstractString)
|
||||
if p.gpgsign
|
||||
run(pipeline(`git -C $pkg_dir commit -S --allow-empty -m $msg`; stdout=devnull))
|
||||
else
|
||||
LibGit2.commit(repo, msg)
|
||||
end
|
||||
end
|
||||
|
||||
function render_plugin(p::Git, t::Template)
|
||||
ignore = mapreduce(gitignore, append!, values(t.plugins))
|
||||
# Only ignore manifests at the repo root.
|
||||
t.manifest || "Manifest.toml" in entries || push!(entries, "/Manifest.toml")
|
||||
unique!(sort!(entries))
|
||||
return join(entries, "\n")
|
||||
end
|
||||
|
||||
function gen_plugin(p::Gitignore, t::Template, pkg_dir::AbstractString)
|
||||
t.git && gen_file(joinpath(pkg_dir, ".gitignore"), render_plugin(p, t))
|
||||
p.manifest || "Manifest.toml" in ignore || push!(ignore, "/Manifest.toml")
|
||||
unique!(sort!(ignore))
|
||||
return join(ignore, "\n")
|
||||
end
|
||||
|
||||
"""
|
||||
|
@ -163,9 +267,18 @@ source(p::Tests) = p.file
|
|||
destination(::Tests) = joinpath("test", "runtests.jl")
|
||||
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
||||
|
||||
function gen_plugin(p::Tests, t::Template, pkg_dir::AbstractString)
|
||||
function prehook(p::Tests, t::Template, pkg_dir::AbstractString)
|
||||
invoke(prehook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
p.project && t.julia_version < v"1.2" && @warn string(
|
||||
"Tests: The project option is set to create a project (supported in Julia 1.2 and later) ",
|
||||
"but a Julia version older than 1.2 is supported by the Template.",
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function hook(p::Tests, t::Template, pkg_dir::AbstractString)
|
||||
# Do the normal BasicPlugin behaviour to create the test script.
|
||||
invoke(gen_plugin, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
invoke(hook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
||||
|
||||
# Then set up the test depdendency in the chosen way.
|
||||
f = p.project ? make_test_project : add_test_dependency
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
"""
|
||||
Develop()
|
||||
|
||||
Adds generated packages to the current environment by `dev`ing them.
|
||||
See [here](https://julialang.github.io/Pkg.jl/v1/managing-packages/#Developing-packages-1) for more details.
|
||||
"""
|
||||
struct Develop <: Plugin end
|
||||
|
||||
function posthook(::Develop, ::Template, pkg_dir::AbstractString)
|
||||
Pkg.develop(PackageSpec(; path=pkg_dir))
|
||||
end
|
|
@ -85,14 +85,14 @@ function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString)
|
|||
return merge(base, Dict("HAS_DEPLOY" => true))
|
||||
end
|
||||
|
||||
function gen_plugin(p::Documenter, t::Template, pkg_dir::AbstractString)
|
||||
function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
|
||||
pkg = basename(pkg_dir)
|
||||
docs_dir = joinpath(pkg_dir, "docs")
|
||||
|
||||
# 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, "make.jl"), make)
|
||||
gen_file(joinpath(docs_dir, "src", "index.md"), index)
|
||||
|
||||
# Copy over any assets.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
default_plugins() = [Gitignore(), License(), Readme(), Tests()]
|
||||
default_plugins() = [ProjectFile(), SrcDir(), Git(), License(), Readme(), Tests()]
|
||||
default_user() = LibGit2.getconfig("github.user", "")
|
||||
default_version() = VersionNumber(VERSION.major)
|
||||
|
||||
|
@ -26,20 +26,14 @@ A configuration used to generate packages.
|
|||
|
||||
### 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.
|
||||
|
||||
### Git Options
|
||||
- `git::Bool=true`: Whether or not to create a Git repository for new packages.
|
||||
- `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 left unset, HTTPS will be used.
|
||||
- `manifest::Bool=false`: Whether or not to commit the `Manifest.toml`.
|
||||
|
||||
### 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 [`Readme`](@ref), [`License`](@ref), [`Tests`](@ref), and [`Gitignore`](@ref).
|
||||
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
|
||||
|
@ -57,14 +51,10 @@ julia> t("PkgName")
|
|||
"""
|
||||
struct Template
|
||||
authors::Vector{String}
|
||||
develop::Bool
|
||||
dir::String
|
||||
git::Bool
|
||||
host::String
|
||||
julia_version::VersionNumber
|
||||
manifest::Bool
|
||||
plugins::Dict{DataType, <:Plugin}
|
||||
ssh::Bool
|
||||
user::String
|
||||
end
|
||||
|
||||
|
@ -78,9 +68,9 @@ function Template(::Val{false}; kwargs...)
|
|||
authors = getkw(kwargs, :authors)
|
||||
authors isa Vector || (authors = map(strip, split(authors, ",")))
|
||||
|
||||
host = replace(getkw(kwargs, :host), r".*://" => "")
|
||||
|
||||
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())
|
||||
|
@ -89,26 +79,7 @@ function Template(::Val{false}; kwargs...)
|
|||
# which means that default plugins get replaced by user values.
|
||||
plugins = Dict(typeof(p) => p for p in enabled)
|
||||
|
||||
# TODO: It might be nice to offer some kind of warn_incompatible function
|
||||
# to be optionally implemented by plugins instead of hardcoding this case here.
|
||||
julia = getkw(kwargs, :julia_version)
|
||||
julia < v"1.2" && haskey(plugins, Tests) && plugins[Tests].project && @warn string(
|
||||
"The Tests plugin is set to create a project (supported in Julia 1.2 and later)",
|
||||
"but a Julia version older than 1.2 is supported.",
|
||||
)
|
||||
|
||||
return Template(
|
||||
authors,
|
||||
getkw(kwargs, :develop),
|
||||
dir,
|
||||
getkw(kwargs, :git),
|
||||
host,
|
||||
julia,
|
||||
getkw(kwargs, :manifest),
|
||||
plugins,
|
||||
getkw(kwargs, :ssh),
|
||||
user,
|
||||
)
|
||||
return Template(authors, dir, host, julia_version, plugins, user)
|
||||
end
|
||||
|
||||
# Does the template have a plugin that satisfies some predicate?
|
||||
|
@ -121,13 +92,35 @@ 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{:develop}) = true
|
||||
defaultkw(::Val{:dir}) = Pkg.devdir()
|
||||
defaultkw(::Val{:disable_defaults}) = DataType[]
|
||||
defaultkw(::Val{:git}) = true
|
||||
defaultkw(::Val{:host}) = "github.com"
|
||||
defaultkw(::Val{:julia_version}) = default_version()
|
||||
defaultkw(::Val{:manifest}) = false
|
||||
defaultkw(::Val{:plugins}) = Plugin[]
|
||||
defaultkw(::Val{:ssh}) = false
|
||||
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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module {{{PKG}}}
|
||||
|
||||
# Write your package code here.
|
||||
|
||||
end
|
Loading…
Reference in New Issue