Move the default plugins into their own files, fix some bugs
This commit is contained in:
parent
146c1bdbe5
commit
2a5fb5ccc8
@ -1,4 +1,4 @@
|
|||||||
const DEFAULTS_DIR = normpath(joinpath(@__DIR__, "..", "templates"))
|
const TEMPLATES_DIR = normpath(joinpath(@__DIR__, "..", "templates"))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A simple plugin that, in general, creates a single file.
|
A simple plugin that, in general, creates a single file.
|
||||||
@ -8,9 +8,9 @@ abstract type BasicPlugin <: Plugin end
|
|||||||
"""
|
"""
|
||||||
default_file(paths::AbstractString...) -> String
|
default_file(paths::AbstractString...) -> String
|
||||||
|
|
||||||
Return a path relative to the default template file directory (`$(contractuser(DEFAULTS_DIR))`).
|
Return a path relative to the default template file directory (`$(contractuser(TEMPLATES_DIR))`).
|
||||||
"""
|
"""
|
||||||
default_file(paths::AbstractString...) = joinpath(DEFAULTS_DIR, paths...)
|
default_file(paths::AbstractString...) = joinpath(TEMPLATES_DIR, paths...)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}
|
view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}
|
||||||
@ -205,9 +205,14 @@ function render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=no
|
|||||||
return tags === nothing ? render(text, view) : render(text, view; tags=tags)
|
return tags === nothing ? render(text, view) : render(text, view; tags=tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
include(joinpath("plugins", "defaults.jl"))
|
include(joinpath("plugins", "project_file.jl"))
|
||||||
|
include(joinpath("plugins", "src_dir.jl"))
|
||||||
|
include(joinpath("plugins", "tests.jl"))
|
||||||
|
include(joinpath("plugins", "readme.jl"))
|
||||||
|
include(joinpath("plugins", "license.jl"))
|
||||||
|
include(joinpath("plugins", "git.jl"))
|
||||||
|
include(joinpath("plugins", "develop.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"))
|
||||||
include(joinpath("plugins", "develop.jl"))
|
|
||||||
include(joinpath("plugins", "documenter.jl"))
|
include(joinpath("plugins", "documenter.jl"))
|
||||||
|
@ -1,306 +0,0 @@
|
|||||||
const TEST_UUID = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
|
||||||
const TEST_DEP = PackageSpec(; name="Test", uuid=TEST_UUID)
|
|
||||||
|
|
||||||
badge_order() = [
|
|
||||||
Documenter{GitLabCI},
|
|
||||||
Documenter{TravisCI},
|
|
||||||
GitLabCI,
|
|
||||||
TravisCI,
|
|
||||||
AppVeyor,
|
|
||||||
CirrusCI,
|
|
||||||
Codecov,
|
|
||||||
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")))",
|
|
||||||
destination="README.md",
|
|
||||||
inline_badges=false,
|
|
||||||
)
|
|
||||||
|
|
||||||
Creates a `README` file.
|
|
||||||
By default, it includes badges for other included plugins
|
|
||||||
|
|
||||||
## Keyword Arguments
|
|
||||||
- `file::AbstractString`: Template file for the `README`.
|
|
||||||
- `destination::AbstractString`: File destination, relative to the repository root.
|
|
||||||
For example, values of `"README"` or `"README.rst"` might be desired.
|
|
||||||
- `inline_badges::Bool`: Whether or not to put the badges on the same line as the package name.
|
|
||||||
"""
|
|
||||||
@with_kw_noshow struct Readme <: BasicPlugin
|
|
||||||
file::String = default_file("README.md")
|
|
||||||
destination::String = "README.md"
|
|
||||||
inline_badges::Bool = false
|
|
||||||
end
|
|
||||||
|
|
||||||
source(p::Readme) = p.file
|
|
||||||
destination(p::Readme) = p.destination
|
|
||||||
|
|
||||||
function view(p::Readme, t::Template, pkg::AbstractString)
|
|
||||||
# Explicitly ordered badges go first.
|
|
||||||
strings = String[]
|
|
||||||
done = DataType[]
|
|
||||||
foreach(badge_order()) do T
|
|
||||||
if hasplugin(t, T)
|
|
||||||
bs = badges(t.plugins[T], t, pkg)
|
|
||||||
append!(strings, badges(t.plugins[T], t, pkg))
|
|
||||||
push!(done, T)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
foreach(setdiff(keys(t.plugins), done)) do T
|
|
||||||
bs = badges(t.plugins[T], t, pkg)
|
|
||||||
append!(strings, badges(t.plugins[T], t, pkg))
|
|
||||||
end
|
|
||||||
|
|
||||||
return Dict(
|
|
||||||
"BADGES" => strings,
|
|
||||||
"HAS_CITATION" => hasplugin(t, Citation) && t.plugins[Citation].readme,
|
|
||||||
"HAS_INLINE_BADGES" => !isempty(strings) && p.inline_badges,
|
|
||||||
"PKG" => pkg,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
License(; name="MIT", path=nothing, destination="LICENSE")
|
|
||||||
|
|
||||||
Creates a license file.
|
|
||||||
|
|
||||||
## Keyword Arguments
|
|
||||||
- `name::AbstractString`: Name of a license supported by PkgTemplates.
|
|
||||||
Available licenses can be seen [here](https://github.com/invenia/PkgTemplates.jl/tree/master/templates/licenses).
|
|
||||||
- `path::Union{AbstractString, Nothing}`: Path to a custom license file.
|
|
||||||
This keyword takes priority over `name`.
|
|
||||||
- `destination::AbstractString`: File destination, relative to the repository root.
|
|
||||||
For example, `"LICENSE.md"` might be desired.
|
|
||||||
"""
|
|
||||||
struct License <: BasicPlugin
|
|
||||||
path::String
|
|
||||||
destination::String
|
|
||||||
end
|
|
||||||
|
|
||||||
function License(
|
|
||||||
name::AbstractString="MIT",
|
|
||||||
path::Union{AbstractString, Nothing}=nothing,
|
|
||||||
destination::AbstractString="LICENSE",
|
|
||||||
)
|
|
||||||
if path === nothing
|
|
||||||
path = default_file("licenses", name)
|
|
||||||
isfile(path) || throw(ArgumentError("License '$(basename(path))' is not available"))
|
|
||||||
end
|
|
||||||
return License(path, destination)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
source(p::License) = p.path
|
|
||||||
destination(p::License) = p.destination
|
|
||||||
view(::License, t::Template, ::AbstractString) = Dict(
|
|
||||||
"AUTHORS" => join(t.authors, ", "),
|
|
||||||
"YEAR" => year(today()),
|
|
||||||
)
|
|
||||||
|
|
||||||
"""
|
|
||||||
Git(; ignore=String[], ssh=false, manifest=false, gpgsign=false)
|
|
||||||
|
|
||||||
Creates a Git repository and a `.gitignore` file.
|
|
||||||
|
|
||||||
## Keyword Arguments
|
|
||||||
- `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 Git <: Plugin
|
|
||||||
ignore::Vector{String} = []
|
|
||||||
ssh::Bool = false
|
|
||||||
manifest::Bool = false
|
|
||||||
gpgsign::Bool = false
|
|
||||||
end
|
|
||||||
|
|
||||||
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.
|
|
||||||
p.manifest || "Manifest.toml" in ignore || push!(ignore, "/Manifest.toml")
|
|
||||||
unique!(sort!(ignore))
|
|
||||||
return join(ignore, "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
Tests(; file="$(contractuser(default_file("test", "runtests.jl")))", project=false)
|
|
||||||
|
|
||||||
Sets up testing for packages.
|
|
||||||
|
|
||||||
## Keyword Arguments
|
|
||||||
- `file::AbstractString`: Template file for the `runtests.jl`.
|
|
||||||
- `project::Bool`: Whether or not to create a new project for tests (`test/Project.toml`).
|
|
||||||
See [here](https://julialang.github.io/Pkg.jl/v1/creating-packages/#Test-specific-dependencies-in-Julia-1.2-and-above-1) for more details.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Managing test dependencies with `test/Project.toml` is only supported in Julia 1.2 and later.
|
|
||||||
"""
|
|
||||||
@with_kw_noshow struct Tests <: BasicPlugin
|
|
||||||
file::String = default_file("test", "runtests.jl")
|
|
||||||
project::Bool = false
|
|
||||||
end
|
|
||||||
|
|
||||||
source(p::Tests) = p.file
|
|
||||||
destination(::Tests) = joinpath("test", "runtests.jl")
|
|
||||||
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
|
||||||
|
|
||||||
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(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
|
|
||||||
f(pkg_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a new test project.
|
|
||||||
function make_test_project(pkg_dir::AbstractString)
|
|
||||||
with_project(() -> Pkg.add(TEST_DEP), joinpath(pkg_dir, "test"))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add Test as a test-only dependency.
|
|
||||||
function add_test_dependency(pkg_dir::AbstractString)
|
|
||||||
# Add the dependency manually since there's no programmatic way to add to [extras].
|
|
||||||
path = joinpath(pkg_dir, "Project.toml")
|
|
||||||
toml = TOML.parsefile(path)
|
|
||||||
get!(toml, "extras", Dict())["Test"] = TEST_UUID
|
|
||||||
get!(toml, "targets", Dict())["test"] = ["Test"]
|
|
||||||
open(io -> TOML.print(io, toml), path, "w")
|
|
||||||
|
|
||||||
# Generate the manifest by updating the project.
|
|
||||||
# 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.
|
|
||||||
with_project(Pkg.update, pkg_dir)
|
|
||||||
end
|
|
87
src/plugins/git.jl
Normal file
87
src/plugins/git.jl
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
"""
|
||||||
|
Git(; ignore=String[], ssh=false, manifest=false, gpgsign=false)
|
||||||
|
|
||||||
|
Creates a Git repository and a `.gitignore` file.
|
||||||
|
|
||||||
|
## Keyword Arguments
|
||||||
|
- `ignore::Vector{<:AbstractString}`: Patterns to add to the `.gitignore`.
|
||||||
|
See also: [`gitignore`](@ref).
|
||||||
|
- `manifest::Bool`: Whether or not to commit `Manifest.toml`.
|
||||||
|
- `ssh::Bool`: Whether or not to use SSH for the remote.
|
||||||
|
If left unset, HTTPS is used.
|
||||||
|
- `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 Git <: Plugin
|
||||||
|
ignore::Vector{String} = []
|
||||||
|
ssh::Bool = false
|
||||||
|
manifest::Bool = false
|
||||||
|
gpgsign::Bool = false
|
||||||
|
ignore_manifest::Bool = true
|
||||||
|
end
|
||||||
|
|
||||||
|
Base.:(==)(a::Git, b::Git) = all(map(n -> getfield(a, n) == getfield(b, n), fieldnames(Git)))
|
||||||
|
|
||||||
|
function gitignore(p::Git)
|
||||||
|
ignore = copy(p.ignore)
|
||||||
|
p.manifest || push!(ignore, "Manifest.toml")
|
||||||
|
return ignore
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
ignore = mapreduce(gitignore, append!, values(t.plugins))
|
||||||
|
# Only ignore manifests at the repo root.
|
||||||
|
p.manifest || "Manifest.toml" in ignore || push!(ignore, "/Manifest.toml")
|
||||||
|
unique!(sort!(ignore))
|
||||||
|
gen_file(joinpath(pkg_dir, ".gitignore"), join(ignore, "\n"))
|
||||||
|
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
|
37
src/plugins/license.jl
Normal file
37
src/plugins/license.jl
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
License(; name="MIT", path=nothing, destination="LICENSE")
|
||||||
|
|
||||||
|
Creates a license file.
|
||||||
|
|
||||||
|
## Keyword Arguments
|
||||||
|
- `name::AbstractString`: Name of a license supported by PkgTemplates.
|
||||||
|
Available licenses can be seen [here](https://github.com/invenia/PkgTemplates.jl/tree/master/templates/licenses).
|
||||||
|
- `path::Union{AbstractString, Nothing}`: Path to a custom license file.
|
||||||
|
This keyword takes priority over `name`.
|
||||||
|
- `destination::AbstractString`: File destination, relative to the repository root.
|
||||||
|
For example, `"LICENSE.md"` might be desired.
|
||||||
|
"""
|
||||||
|
struct License <: BasicPlugin
|
||||||
|
path::String
|
||||||
|
destination::String
|
||||||
|
end
|
||||||
|
|
||||||
|
function License(
|
||||||
|
name::AbstractString="MIT",
|
||||||
|
path::Union{AbstractString, Nothing}=nothing,
|
||||||
|
destination::AbstractString="LICENSE",
|
||||||
|
)
|
||||||
|
if path === nothing
|
||||||
|
path = default_file("licenses", name)
|
||||||
|
isfile(path) || throw(ArgumentError("License '$(basename(path))' is not available"))
|
||||||
|
end
|
||||||
|
return License(path, destination)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
source(p::License) = p.path
|
||||||
|
destination(p::License) = p.destination
|
||||||
|
view(::License, t::Template, ::AbstractString) = Dict(
|
||||||
|
"AUTHORS" => join(t.authors, ", "),
|
||||||
|
"YEAR" => year(today()),
|
||||||
|
)
|
33
src/plugins/project_file.jl
Normal file
33
src/plugins/project_file.jl
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"""
|
||||||
|
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,
|
||||||
|
"version" => "0.1.0",
|
||||||
|
"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
|
59
src/plugins/readme.jl
Normal file
59
src/plugins/readme.jl
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Readme(;
|
||||||
|
file="$(contractuser(default_file("README.md")))",
|
||||||
|
destination="README.md",
|
||||||
|
inline_badges=false,
|
||||||
|
)
|
||||||
|
|
||||||
|
Creates a `README` file.
|
||||||
|
By default, it includes badges for other included plugins
|
||||||
|
|
||||||
|
## Keyword Arguments
|
||||||
|
- `file::AbstractString`: Template file for the `README`.
|
||||||
|
- `destination::AbstractString`: File destination, relative to the repository root.
|
||||||
|
For example, values of `"README"` or `"README.rst"` might be desired.
|
||||||
|
- `inline_badges::Bool`: Whether or not to put the badges on the same line as the package name.
|
||||||
|
"""
|
||||||
|
@with_kw_noshow struct Readme <: BasicPlugin
|
||||||
|
file::String = default_file("README.md")
|
||||||
|
destination::String = "README.md"
|
||||||
|
inline_badges::Bool = false
|
||||||
|
end
|
||||||
|
|
||||||
|
source(p::Readme) = p.file
|
||||||
|
destination(p::Readme) = p.destination
|
||||||
|
|
||||||
|
function view(p::Readme, t::Template, pkg::AbstractString)
|
||||||
|
# Explicitly ordered badges go first.
|
||||||
|
strings = String[]
|
||||||
|
done = DataType[]
|
||||||
|
foreach(badge_order()) do T
|
||||||
|
if hasplugin(t, T)
|
||||||
|
bs = badges(t.plugins[T], t, pkg)
|
||||||
|
append!(strings, badges(t.plugins[T], t, pkg))
|
||||||
|
push!(done, T)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
foreach(setdiff(keys(t.plugins), done)) do T
|
||||||
|
bs = badges(t.plugins[T], t, pkg)
|
||||||
|
append!(strings, badges(t.plugins[T], t, pkg))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Dict(
|
||||||
|
"BADGES" => strings,
|
||||||
|
"HAS_CITATION" => hasplugin(t, Citation) && t.plugins[Citation].readme,
|
||||||
|
"HAS_INLINE_BADGES" => !isempty(strings) && p.inline_badges,
|
||||||
|
"PKG" => pkg,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
badge_order() = [
|
||||||
|
Documenter{GitLabCI},
|
||||||
|
Documenter{TravisCI},
|
||||||
|
GitLabCI,
|
||||||
|
TravisCI,
|
||||||
|
AppVeyor,
|
||||||
|
CirrusCI,
|
||||||
|
Codecov,
|
||||||
|
Coveralls,
|
||||||
|
]
|
29
src/plugins/src_dir.jl
Normal file
29
src/plugins/src_dir.jl
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
Base.:(==)(a::SrcDir, b::SrcDir) = a.file == b.file
|
||||||
|
|
||||||
|
# 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
|
62
src/plugins/tests.jl
Normal file
62
src/plugins/tests.jl
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
const TEST_UUID = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
const TEST_DEP = PackageSpec(; name="Test", uuid=TEST_UUID)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tests(; file="$(contractuser(default_file("test", "runtests.jl")))", project=false)
|
||||||
|
|
||||||
|
Sets up testing for packages.
|
||||||
|
|
||||||
|
## Keyword Arguments
|
||||||
|
- `file::AbstractString`: Template file for the `runtests.jl`.
|
||||||
|
- `project::Bool`: Whether or not to create a new project for tests (`test/Project.toml`).
|
||||||
|
See [here](https://julialang.github.io/Pkg.jl/v1/creating-packages/#Test-specific-dependencies-in-Julia-1.2-and-above-1) for more details.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Managing test dependencies with `test/Project.toml` is only supported in Julia 1.2 and later.
|
||||||
|
"""
|
||||||
|
@with_kw_noshow struct Tests <: BasicPlugin
|
||||||
|
file::String = default_file("test", "runtests.jl")
|
||||||
|
project::Bool = false
|
||||||
|
end
|
||||||
|
|
||||||
|
source(p::Tests) = p.file
|
||||||
|
destination(::Tests) = joinpath("test", "runtests.jl")
|
||||||
|
view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
|
||||||
|
|
||||||
|
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(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
|
||||||
|
f(pkg_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new test project.
|
||||||
|
function make_test_project(pkg_dir::AbstractString)
|
||||||
|
with_project(() -> Pkg.add(TEST_DEP), joinpath(pkg_dir, "test"))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add Test as a test-only dependency.
|
||||||
|
function add_test_dependency(pkg_dir::AbstractString)
|
||||||
|
# Add the dependency manually since there's no programmatic way to add to [extras].
|
||||||
|
path = joinpath(pkg_dir, "Project.toml")
|
||||||
|
toml = TOML.parsefile(path)
|
||||||
|
get!(toml, "extras", Dict())["Test"] = TEST_UUID
|
||||||
|
get!(toml, "targets", Dict())["test"] = ["Test"]
|
||||||
|
open(io -> TOML.print(io, toml), path, "w")
|
||||||
|
|
||||||
|
# Generate the manifest by updating the project.
|
||||||
|
# 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.
|
||||||
|
with_project(Pkg.update, pkg_dir)
|
||||||
|
end
|
3
test/fixtures/AllPlugins/.gitignore
vendored
3
test/fixtures/AllPlugins/.gitignore
vendored
@ -1,7 +1,6 @@
|
|||||||
*.jl.*.cov
|
*.jl.*.cov
|
||||||
*.jl.cov
|
*.jl.cov
|
||||||
*.jl.mem
|
*.jl.mem
|
||||||
.DS_Store
|
|
||||||
/dev/
|
|
||||||
/docs/build/
|
/docs/build/
|
||||||
/docs/site/
|
/docs/site/
|
||||||
|
Manifest.toml
|
||||||
|
4
test/fixtures/AllPlugins/src/AllPlugins.jl
vendored
4
test/fixtures/AllPlugins/src/AllPlugins.jl
vendored
@ -1,5 +1,5 @@
|
|||||||
module AllPlugins
|
module AllPlugins
|
||||||
|
|
||||||
greet() = print("Hello World!")
|
# Write your package code here.
|
||||||
|
|
||||||
end # module
|
end
|
||||||
|
3
test/fixtures/Basic/.gitignore
vendored
3
test/fixtures/Basic/.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
.DS_Store
|
Manifest.toml
|
||||||
/dev/
|
|
||||||
|
4
test/fixtures/Basic/src/Basic.jl
vendored
4
test/fixtures/Basic/src/Basic.jl
vendored
@ -1,5 +1,5 @@
|
|||||||
module Basic
|
module Basic
|
||||||
|
|
||||||
greet() = print("Hello World!")
|
# Write your package code here.
|
||||||
|
|
||||||
end # module
|
end
|
||||||
|
10
test/git.jl
10
test/git.jl
@ -6,7 +6,7 @@ end
|
|||||||
|
|
||||||
@testset "Git repositories" begin
|
@testset "Git repositories" begin
|
||||||
@testset "Does not create Git repo" begin
|
@testset "Does not create Git repo" begin
|
||||||
t = tpl(; git=false)
|
t = tpl(; disable_defaults=[Git])
|
||||||
with_pkg(t) do pkg
|
with_pkg(t) do pkg
|
||||||
pkg_dir = joinpath(t.dir, pkg)
|
pkg_dir = joinpath(t.dir, pkg)
|
||||||
@test !isdir(joinpath(pkg_dir, ".git"))
|
@test !isdir(joinpath(pkg_dir, ".git"))
|
||||||
@ -14,7 +14,7 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "Creates Git repo" begin
|
@testset "Creates Git repo" begin
|
||||||
t = tpl(; git=true)
|
t = tpl(; plugins=[Git()])
|
||||||
with_pkg(t) do pkg
|
with_pkg(t) do pkg
|
||||||
pkg_dir = joinpath(t.dir, pkg)
|
pkg_dir = joinpath(t.dir, pkg)
|
||||||
@test isdir(joinpath(pkg_dir, ".git"))
|
@test isdir(joinpath(pkg_dir, ".git"))
|
||||||
@ -22,7 +22,7 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "With HTTPS" begin
|
@testset "With HTTPS" begin
|
||||||
t = tpl(; git=true, ssh=false)
|
t = tpl(; plugins=[Git(; ssh=false)])
|
||||||
with_pkg(t) do pkg
|
with_pkg(t) do pkg
|
||||||
LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo
|
LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo
|
||||||
remote = LibGit2.get(GitRemote, repo, "origin")
|
remote = LibGit2.get(GitRemote, repo, "origin")
|
||||||
@ -32,7 +32,7 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
@testset "With SSH" begin
|
@testset "With SSH" begin
|
||||||
t = tpl(; git=true, ssh=true)
|
t = tpl(; plugins=[Git(; ssh=true)])
|
||||||
with_pkg(t) do pkg
|
with_pkg(t) do pkg
|
||||||
LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo
|
LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo
|
||||||
remote = LibGit2.get(GitRemote, repo, "origin")
|
remote = LibGit2.get(GitRemote, repo, "origin")
|
||||||
@ -43,7 +43,7 @@ end
|
|||||||
|
|
||||||
@testset "Adds version to commit message" begin
|
@testset "Adds version to commit message" begin
|
||||||
# We're careful to avoid a Pkg.update as it triggers Cassette#130.
|
# We're careful to avoid a Pkg.update as it triggers Cassette#130.
|
||||||
t = tpl(; git=true, develop=false, disable_defaults=[Tests])
|
t = tpl(; disable_defaults=[Tests], plugins=[Git()])
|
||||||
@overdub PTIsInstalled() with_pkg(t) do pkg
|
@overdub PTIsInstalled() with_pkg(t) do pkg
|
||||||
pkg_dir = joinpath(t.dir, pkg)
|
pkg_dir = joinpath(t.dir, pkg)
|
||||||
LibGit2.with(GitRepo(pkg_dir)) do repo
|
LibGit2.with(GitRepo(pkg_dir)) do repo
|
||||||
|
@ -23,8 +23,12 @@ end
|
|||||||
|
|
||||||
@testset "All plugins" begin
|
@testset "All plugins" begin
|
||||||
test_all("AllPlugins"; authors=USER, manifest=true, plugins=[
|
test_all("AllPlugins"; authors=USER, manifest=true, plugins=[
|
||||||
AppVeyor(), CirrusCI(), Citation(), Codecov(),
|
AppVeyor(), CirrusCI(), Citation(), Codecov(), Coveralls(),
|
||||||
Coveralls(), Documenter(), GitLabCI(), TravisCI(),
|
Develop(), Documenter(), GitLabCI(), TravisCI(),
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@testset "Wacky options" begin
|
||||||
|
# TODO
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,10 +19,10 @@ Random.seed!(1)
|
|||||||
# Creata a template that won't error because of a missing username.
|
# Creata a template that won't error because of a missing username.
|
||||||
tpl(; kwargs...) = Template(; user=USER, kwargs...)
|
tpl(; kwargs...) = Template(; user=USER, kwargs...)
|
||||||
|
|
||||||
const pkg_name = Ref("A")
|
const PKG = Ref("A")
|
||||||
|
|
||||||
# Generate an unused package name.
|
# Generate an unused package name.
|
||||||
pkgname() = pkg_name[] *= "a"
|
pkgname() = PKG[] *= "a"
|
||||||
|
|
||||||
# Create a randomly named package with a template, and delete it afterwards.
|
# Create a randomly named package with a template, and delete it afterwards.
|
||||||
function with_pkg(f::Function, t::Template, pkg::AbstractString=pkgname())
|
function with_pkg(f::Function, t::Template, pkg::AbstractString=pkgname())
|
||||||
|
41
test/show.jl
41
test/show.jl
@ -1,11 +1,11 @@
|
|||||||
const DEFAULTS_DIR = contractuser(PT.DEFAULTS_DIR)
|
const TEMPLATES_DIR = contractuser(PT.TEMPLATES_DIR)
|
||||||
const LICENSE_DIR = contractuser(joinpath(PT.DEFAULTS_DIR, "licenses"))
|
const LICENSES_DIR = joinpath(TEMPLATES_DIR, "licenses")
|
||||||
|
|
||||||
@testset "Show methods" begin
|
@testset "Show methods" begin
|
||||||
@testset "Plugins" begin
|
@testset "Plugins" begin
|
||||||
expected = """
|
expected = """
|
||||||
Readme:
|
Readme:
|
||||||
file: "$(joinpath(DEFAULTS_DIR, "README.md"))"
|
file: "$(joinpath(TEMPLATES_DIR, "README.md"))"
|
||||||
destination: "README.md"
|
destination: "README.md"
|
||||||
inline_badges: false
|
inline_badges: false
|
||||||
"""
|
"""
|
||||||
@ -15,28 +15,39 @@ const LICENSE_DIR = contractuser(joinpath(PT.DEFAULTS_DIR, "licenses"))
|
|||||||
@testset "Template" begin
|
@testset "Template" begin
|
||||||
expected = """
|
expected = """
|
||||||
Template:
|
Template:
|
||||||
authors: ["$USER"]
|
authors: ["Chris de Graaf <chrisadegraaf@gmail.com>"]
|
||||||
develop: true
|
dir: "~/.local/share/julia/dev"
|
||||||
dir: "$(contractuser(Pkg.devdir()))"
|
|
||||||
git: true
|
|
||||||
host: "github.com"
|
host: "github.com"
|
||||||
julia_version: v"1.0.0"
|
julia_version: v"1.0.0"
|
||||||
manifest: false
|
|
||||||
ssh: false
|
|
||||||
user: "$USER"
|
user: "$USER"
|
||||||
plugins:
|
plugins:
|
||||||
Gitignore:
|
"""
|
||||||
ds_store: true
|
expected = """
|
||||||
dev: true
|
Template:
|
||||||
|
authors: ["$USER"]
|
||||||
|
dir: "$(contractuser(Pkg.devdir()))"
|
||||||
|
host: "github.com"
|
||||||
|
julia_version: v"1.0.0"
|
||||||
|
user: "$USER"
|
||||||
|
plugins:
|
||||||
|
Git:
|
||||||
|
ignore: String[]
|
||||||
|
ssh: false
|
||||||
|
manifest: false
|
||||||
|
gpgsign: false
|
||||||
|
ignore_manifest: true
|
||||||
License:
|
License:
|
||||||
path: "$(joinpath(LICENSE_DIR, "MIT"))"
|
path: "$(joinpath(LICENSES_DIR, "MIT"))"
|
||||||
destination: "LICENSE"
|
destination: "LICENSE"
|
||||||
|
ProjectFile:
|
||||||
Readme:
|
Readme:
|
||||||
file: "$(joinpath(DEFAULTS_DIR, "README.md"))"
|
file: "$(joinpath(TEMPLATES_DIR, "README.md"))"
|
||||||
destination: "README.md"
|
destination: "README.md"
|
||||||
inline_badges: false
|
inline_badges: false
|
||||||
|
SrcDir:
|
||||||
|
file: "$(joinpath(TEMPLATES_DIR, "src", "module.jl"))"
|
||||||
Tests:
|
Tests:
|
||||||
file: "$(joinpath(DEFAULTS_DIR, "test", "runtests.jl"))"
|
file: "$(joinpath(TEMPLATES_DIR, "test", "runtests.jl"))"
|
||||||
project: false
|
project: false
|
||||||
"""
|
"""
|
||||||
@test sprint(show, MIME("text/plain"), tpl(; authors=USER)) == rstrip(expected)
|
@test sprint(show, MIME("text/plain"), tpl(; authors=USER)) == rstrip(expected)
|
||||||
|
@ -35,10 +35,11 @@
|
|||||||
test_plugins([], defaults)
|
test_plugins([], defaults)
|
||||||
test_plugins([Citation()], union(defaults, [Citation()]))
|
test_plugins([Citation()], union(defaults, [Citation()]))
|
||||||
# Overriding a default plugin.
|
# Overriding a default plugin.
|
||||||
gi = Gitignore(; dev=false)
|
default_g = defaults[findfirst(p -> p isa Git, defaults)]
|
||||||
test_plugins([gi], union(setdiff(defaults, [Gitignore()]), [gi]))
|
g = Git(; ssh=true)
|
||||||
|
test_plugins([g], union(setdiff(defaults, [default_g]), [g]))
|
||||||
# Disabling a default plugin.
|
# Disabling a default plugin.
|
||||||
test_plugins([], setdiff(defaults, [Gitignore()]), [Gitignore])
|
test_plugins([], setdiff(defaults, [default_g]), [Git])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user