Add per-plugin validation at template construction time
This commit is contained in:
parent
385195c6b5
commit
fd995a05bc
@ -17,9 +17,25 @@ Plugin
|
|||||||
BasicPlugin
|
BasicPlugin
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package Generation Pipeline
|
## Template + Package Creation Pipeline
|
||||||
|
|
||||||
The package generation process looks basically like this:
|
|
||||||
|
The [`Template`](@ref) constructor basically does this:
|
||||||
|
|
||||||
|
```
|
||||||
|
- extract values from keyword arguments
|
||||||
|
- create a Template from the values
|
||||||
|
- validate each plugin against the constructed Template
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugin validation step uses the [`validate`](@ref) function.
|
||||||
|
It lets us catch mistakes before we try to generate packages.
|
||||||
|
|
||||||
|
```@docs
|
||||||
|
validate
|
||||||
|
```
|
||||||
|
|
||||||
|
The package generation process looks like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
- create empty directory for the package
|
- create empty directory for the package
|
||||||
@ -31,7 +47,6 @@ The package generation process looks basically like this:
|
|||||||
- run plugin posthook
|
- run plugin posthook
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it!
|
|
||||||
As you can tell, plugins play a central role in setting up a package.
|
As you can tell, plugins play a central role in setting up a package.
|
||||||
|
|
||||||
The three main entrypoints for plugins to do work are the [`prehook`](@ref), the [`hook`](@ref), and the [`posthook`](@ref).
|
The three main entrypoints for plugins to do work are the [`prehook`](@ref), the [`hook`](@ref), and the [`posthook`](@ref).
|
||||||
@ -151,6 +166,14 @@ struct Git <: Plugin end
|
|||||||
|
|
||||||
priority(::Git, ::typeof(posthook)) = 5
|
priority(::Git, ::typeof(posthook)) = 5
|
||||||
|
|
||||||
|
function validate(::Git, ::Template)
|
||||||
|
foreach(("user.name", "user.email")) do k
|
||||||
|
if isempty(LibGit2.getconfig(k, ""))
|
||||||
|
throw(ArgumentError("Git: Global Git config is missing required value '$k'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function prehook(::Git, t::Template, pkg_dir::AbstractString)
|
function prehook(::Git, t::Template, pkg_dir::AbstractString)
|
||||||
LibGit2.with(LibGit2.init(pkg_dir)) do repo
|
LibGit2.with(LibGit2.init(pkg_dir)) do repo
|
||||||
LibGit2.commit(repo, "Initial commit")
|
LibGit2.commit(repo, "Initial commit")
|
||||||
@ -174,8 +197,9 @@ function posthook(::Git, ::Template, pkg_dir::AbstractString)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
All three hooks are implemented:
|
Validation and all three hooks are implemented:
|
||||||
|
|
||||||
|
- [`validate`](@ref) makes sure that all required Git configuration is present.
|
||||||
- [`prehook`](@ref) creates the Git repository for the package.
|
- [`prehook`](@ref) creates the Git repository for the package.
|
||||||
- [`hook`](@ref) generates the `.gitignore` file, using the special [`gitignore`](@ref) function.
|
- [`hook`](@ref) generates the `.gitignore` file, using the special [`gitignore`](@ref) function.
|
||||||
- [`posthook`](@ref) adds and commits all the generated files.
|
- [`posthook`](@ref) adds and commits all the generated files.
|
||||||
@ -286,8 +310,8 @@ function hook(p::Tests, t::Template, pkg_dir::AbstractString)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
There is also a default [`prehook`](@ref) implementation for [`BasicPlugin`](@ref)s, which checks that the plugin's [`source`](@ref) file exists, and throws an `ArgumentError` otherwise.
|
There is also a default [`validate`](@ref) implementation for [`BasicPlugin`](@ref)s, which checks that the plugin's [`source`](@ref) file exists, and throws an `ArgumentError` otherwise.
|
||||||
If you want to extend the prehook but keep the file existence check, use the `invoke` method as described above.
|
If you want to extend the validation but keep the file existence check, use the `invoke` method as described above.
|
||||||
|
|
||||||
For more examples, see the plugins in the [Continuous Integration (CI)](@ref) and [Code Coverage](@ref) sections.
|
For more examples, see the plugins in the [Continuous Integration (CI)](@ref) and [Code Coverage](@ref) sections.
|
||||||
|
|
||||||
|
@ -134,6 +134,15 @@ function badges(p::Plugin, t::Template, pkg::AbstractString)
|
|||||||
return map(b -> render_text(string(b), combined_view(p, t, pkg)), bs)
|
return map(b -> render_text(string(b), combined_view(p, t, pkg)), bs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
validate(::Plugin, ::Template)
|
||||||
|
|
||||||
|
Perform any required validation for a [`Plugin`](@ref).
|
||||||
|
|
||||||
|
It is preferred to do validation here instead of in [`prehook`](@ref), because this function is called at [`Template`](@ref) construction time, whereas the prehook is only run at package generation time.
|
||||||
|
"""
|
||||||
|
validate(::Plugin, ::Template) = nothing
|
||||||
|
|
||||||
"""
|
"""
|
||||||
prehook(::Plugin, ::Template, pkg_dir::AbstractString)
|
prehook(::Plugin, ::Template, pkg_dir::AbstractString)
|
||||||
|
|
||||||
@ -168,7 +177,7 @@ At this point, both the [`prehook`](@ref)s and [`hook`](@ref)s have run.
|
|||||||
"""
|
"""
|
||||||
posthook(::Plugin, ::Template, ::AbstractString) = nothing
|
posthook(::Plugin, ::Template, ::AbstractString) = nothing
|
||||||
|
|
||||||
function prehook(p::T, ::Template, ::AbstractString) where T <: BasicPlugin
|
function validate(p::T, ::Template) where T <: BasicPlugin
|
||||||
src = source(p)
|
src = source(p)
|
||||||
src === nothing && return
|
src === nothing && return
|
||||||
isfile(src) || throw(ArgumentError("$(nameof(T)): The file $src does not exist"))
|
isfile(src) || throw(ArgumentError("$(nameof(T)): The file $src does not exist"))
|
||||||
|
@ -89,6 +89,16 @@ function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString)
|
|||||||
return merge(base, Dict("HAS_DEPLOY" => true))
|
return merge(base, Dict("HAS_DEPLOY" => true))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
foreach((TravisCI, GitLabCI)) do T
|
||||||
|
@eval function validate(::Documenter{$T}, t::Template)
|
||||||
|
if !hasplugin(t, $T)
|
||||||
|
name = nameof($T)
|
||||||
|
s = "Documenter: The $name plugin must be included for docs deployment to be set up"
|
||||||
|
throw(ArgumentError(s))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
|
function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
|
||||||
pkg = basename(pkg_dir)
|
pkg = basename(pkg_dir)
|
||||||
docs_dir = joinpath(pkg_dir, "docs")
|
docs_dir = joinpath(pkg_dir, "docs")
|
||||||
|
@ -30,11 +30,20 @@ function gitignore(p::Git)
|
|||||||
return ignore
|
return ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set up the Git repository.
|
function validate(p::Git, t::Template)
|
||||||
function prehook(p::Git, t::Template, pkg_dir::AbstractString)
|
|
||||||
if p.gpgsign && try run(pipeline(`git --version`; stdout=devnull)); false catch; true end
|
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"))
|
throw(ArgumentError("Git: gpgsign is set but the Git CLI is not installed"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
foreach(("user.name", "user.email")) do k
|
||||||
|
if isempty(LibGit2.getconfig(k, ""))
|
||||||
|
throw(ArgumentError("Git: Global Git config is missing required value '$k'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set up the Git repository.
|
||||||
|
function prehook(p::Git, t::Template, pkg_dir::AbstractString)
|
||||||
LibGit2.with(LibGit2.init(pkg_dir)) do repo
|
LibGit2.with(LibGit2.init(pkg_dir)) do repo
|
||||||
commit(p, repo, pkg_dir, "Initial commit")
|
commit(p, repo, pkg_dir, "Initial commit")
|
||||||
pkg = basename(pkg_dir)
|
pkg = basename(pkg_dir)
|
||||||
|
@ -24,6 +24,5 @@ view(::SrcDir, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
|||||||
# Update the destination now that we know the package name.
|
# Update the destination now that we know the package name.
|
||||||
# Kind of hacky, but oh well.
|
# Kind of hacky, but oh well.
|
||||||
function prehook(p::SrcDir, t::Template, pkg_dir::AbstractString)
|
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")
|
p.destination = joinpath("src", basename(pkg_dir) * ".jl")
|
||||||
end
|
end
|
||||||
|
@ -23,8 +23,8 @@ source(p::Tests) = p.file
|
|||||||
destination(::Tests) = joinpath("test", "runtests.jl")
|
destination(::Tests) = joinpath("test", "runtests.jl")
|
||||||
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
||||||
|
|
||||||
function prehook(p::Tests, t::Template, pkg_dir::AbstractString)
|
function validate(p::Tests, t::Template)
|
||||||
invoke(prehook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
|
invoke(validate, Tuple{BasicPlugin, Template}, p, t)
|
||||||
p.project && t.julia < v"1.2" && @warn string(
|
p.project && t.julia < v"1.2" && @warn string(
|
||||||
"Tests: The project option is set to create a project (supported in Julia 1.2 and later) ",
|
"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.",
|
"but a Julia version older than 1.2 is supported by the Template.",
|
||||||
|
@ -85,7 +85,9 @@ function Template(::Val{false}; kwargs...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Template(authors, dir, host, julia, plugins, user)
|
t = Template(authors, dir, host, julia, plugins, user)
|
||||||
|
foreach(p -> validate(p, t), t.plugins)
|
||||||
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -46,13 +46,19 @@
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "hasplugin" begin
|
@testset "hasplugin" begin
|
||||||
t = tpl(; plugins=[Documenter{TravisCI}()])
|
t = tpl(; plugins=[TravisCI(), Documenter{TravisCI}()])
|
||||||
@test PT.hasplugin(t, typeof(first(PT.default_plugins())))
|
@test PT.hasplugin(t, typeof(first(PT.default_plugins())))
|
||||||
@test PT.hasplugin(t, Documenter)
|
@test PT.hasplugin(t, Documenter)
|
||||||
|
@test PT.hasplugin(t, PT.is_ci)
|
||||||
@test PT.hasplugin(t, _ -> true)
|
@test PT.hasplugin(t, _ -> true)
|
||||||
@test !PT.hasplugin(t, _ -> false)
|
@test !PT.hasplugin(t, _ -> false)
|
||||||
@test !PT.hasplugin(t, Citation)
|
@test !PT.hasplugin(t, Citation)
|
||||||
@test !PT.hasplugin(t, PT.is_ci)
|
end
|
||||||
|
|
||||||
|
@testset "validate" begin
|
||||||
|
mock(LibGit2.getconfig => (_k, _d) -> "") do _gc
|
||||||
|
@test_throws ArgumentError tpl(; plugins=[Git()])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user