diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fae8897 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* eol=lf diff --git a/Project.toml b/Project.toml index 5e069c6..e1ac7d4 100644 --- a/Project.toml +++ b/Project.toml @@ -16,9 +16,11 @@ REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" julia = "1" [extras] +Cassette = "7057c7e9-c182-5462-911a-8362d720325c" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Random", "ReferenceTests", "Test"] +test = ["Cassette", "Suppressor", "Random", "ReferenceTests", "Test"] diff --git a/src/generate.jl b/src/generate.jl index cc5404a..669685b 100644 --- a/src/generate.jl +++ b/src/generate.jl @@ -6,6 +6,8 @@ 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")) + repo = nothing try # Create the directory with some boilerplate inside. @@ -24,12 +26,15 @@ function (t::Template)(pkg::AbstractString) # Initialize the repo, make a commit, and set the remote. repo = LibGit2.init(pkg_dir) LibGit2.commit(repo, "Initial commit") - rmt = if t.ssh + url = if t.ssh "git@$(t.host):$(t.user)/$pkg.jl.git" else "https://$(t.host)/$(t.user)/$pkg.jl" end - close(LibGit2.GitRemote(repo, "origin", rmt)) + remote = LibGit2.GitRemote(repo, "origin", url) + # TODO: `git pull` still requires some Git branch config. + LibGit2.add_push!(repo, remote, "refs/heads/master") + close(remote) end # Generate the files. @@ -56,6 +61,8 @@ function (t::Template)(pkg::AbstractString) catch rm(pkg_dir; recursive=true, force=true) rethrow() + finally + repo === nothing || close(repo) end end diff --git a/test/git.jl b/test/git.jl new file mode 100644 index 0000000..4b14969 --- /dev/null +++ b/test/git.jl @@ -0,0 +1,55 @@ +@context PTIsInstalled +function Cassette.posthook(::PTIsInstalled, result::Dict, ::typeof(Pkg.installed)) + result["PkgTemplates"] = v"1.2.3" + return result +end + +@testset "Git repositories" begin + @testset "Does not create Git repo" begin + t = tpl(; git=false) + with_pkg(t) do pkg + pkg_dir = joinpath(t.dir, pkg) + @test !isdir(joinpath(pkg_dir, ".git")) + end + end + + @testset "Creates Git repo" begin + t = tpl(; git=true) + with_pkg(t) do pkg + pkg_dir = joinpath(t.dir, pkg) + @test isdir(joinpath(pkg_dir, ".git")) + end + end + + @testset "With HTTPS" begin + t = tpl(; git=true, ssh=false) + with_pkg(t) do pkg + LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo + remote = LibGit2.get(GitRemote, repo, "origin") + @test startswith(LibGit2.url(remote), "https://") + end + end + end + + @testset "With SSH" begin + t = tpl(; git=true, ssh=true) + with_pkg(t) do pkg + LibGit2.with(GitRepo(joinpath(t.dir, pkg))) do repo + remote = LibGit2.get(GitRemote, repo, "origin") + @test startswith(LibGit2.url(remote), "git@") + end + end + end + + @testset "Adds version to commit message" begin + # We're careful to avoid a Pkg.update as it triggers Cassette#130. + t = tpl(; git=true, develop=false, disable_defaults=[Tests]) + @overdub PTIsInstalled() with_pkg(t) do pkg + pkg_dir = joinpath(t.dir, pkg) + LibGit2.with(GitRepo(pkg_dir)) do repo + commit = GitCommit(repo, "HEAD") + @test occursin("PkgTemplates version: 1.2.3", LibGit2.message(commit)) + end + end + end +end diff --git a/test/plugin.jl b/test/plugin.jl index b55a47e..05639f1 100644 --- a/test/plugin.jl +++ b/test/plugin.jl @@ -9,21 +9,20 @@ PT.badges(::BasicTest) = PT.Badge("{{X}}", "{{Y}}", "{{Z}}") PT.view(::BasicTest, ::Template, ::AbstractString) = Dict("X" => 0, "Y" => 2) PT.user_view(::BasicTest, ::Template, ::AbstractString) = Dict("X" => 1, "Z" => 3) -@testset "BasicPlugin" begin - p = BasicTest() - t = tpl(; plugins=[p]) +@testset "Plugins" begin + @testset "BasicPlugin" begin + p = BasicTest() + t = tpl(; plugins=[p]) - # The X from user_view should override the X from view. - s = PT.render_plugin(p, t, "") - @test occursin("1 2 3", first(split(s, "\n"))) + # The X from user_view should override the X from view. + s = PT.render_plugin(p, t, "") + @test occursin("1 2 3", first(split(s, "\n"))) - with_pkg(t) do pkg - pkg_dir = joinpath(t.dir, pkg) - badge = string(PT.Badge("1", "2", "3")) - @test occursin(badge, read(joinpath(pkg_dir, "README.md"), String)) - observed = read(joinpath(pkg_dir, "foo.txt"), String) - # On Travis, everything works with CRLF. - Sys.iswindows() && (observed = replace(observed, "\r\n" => "\n")) - @test observed == s + with_pkg(t) do pkg + pkg_dir = joinpath(t.dir, pkg) + badge = string(PT.Badge("1", "2", "3")) + @test occursin(badge, read(joinpath(pkg_dir, "README.md"), String)) + @test read(joinpath(pkg_dir, "foo.txt"), String) == s + end end end diff --git a/test/reference.jl b/test/reference.jl index bafe07a..3a1eaec 100644 --- a/test/reference.jl +++ b/test/reference.jl @@ -17,13 +17,15 @@ function test_all(pkg::AbstractString; kwargs...) end end -@testset "Default package" begin - test_all("Basic"; authors=USER, manifest=true) -end +@testset "Reference tests" begin + @testset "Default package" begin + test_all("Basic"; authors=USER, manifest=true) + end -@testset "All plugins" begin - test_all("AllPlugins"; authors=USER, manifest=true, plugins=[ - AppVeyor(), CirrusCI(), Citation(), Codecov(), - Coveralls(), Documenter(), GitLabCI(), TravisCI(), - ]) + @testset "All plugins" begin + test_all("AllPlugins"; authors=USER, manifest=true, plugins=[ + AppVeyor(), CirrusCI(), Citation(), Codecov(), + Coveralls(), Documenter(), GitLabCI(), TravisCI(), + ]) + end end diff --git a/test/runtests.jl b/test/runtests.jl index 2121039..94312f9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,48 +1,59 @@ using Base.Filesystem: path_separator +using LibGit2: LibGit2, GitCommit, GitRemote, GitRepo using Pkg: Pkg using Random: Random using Test: @test, @testset, @test_throws +using Cassette: Cassette, @context, @overdub using ReferenceTests: @test_reference +using Suppressor: @suppress using PkgTemplates const PT = PkgTemplates -const PKG = "TestPkg" const USER = "tester" Random.seed!(1) +# Creata a template that won't error because of a missing username. tpl(; kwargs...) = Template(; user=USER, kwargs...) -function with_pkg(f::Function, t::Template, pkg::AbstractString=PKG) - t(pkg) +const pkg_name = Ref("A") + +# Generate an unused package name. +pkgname() = pkg_name[] *= "a" + +# Create a randomly named package with a template, and delete it afterwards. +function with_pkg(f::Function, t::Template, pkg::AbstractString=pkgname()) + @suppress t(pkg) try f(pkg) finally - haskey(Pkg.installed(), pkg) && Pkg.rm(pkg) + haskey(Pkg.installed(), pkg) && @suppress Pkg.rm(pkg) rm(joinpath(t.dir, pkg); recursive=true, force=true) end end -@testset "PkgTemplates.jl" begin - mktempdir() do dir - Pkg.activate(dir) - pushfirst!(DEPOT_PATH, dir) - try +mktempdir() do dir + Pkg.activate(dir) + pushfirst!(DEPOT_PATH, dir) + try + @testset "PkgTemplates.jl" begin include("template.jl") include("plugin.jl") + include("git.jl") - # Quite a bit of output depends on the Julia version, and the test fixtures are - # made with Julia 1.2. Also, Windows uses CRLF which breaks everything. - if !Sys.iswindows() && VERSION.major == 1 && VERSION.minor == 2 + # Quite a bit of output depends on the Julia version, + # and the test fixtures are made with Julia 1.2. + # TODO: Keep this on the latest stable Julia version. + if VERSION.major == 1 && VERSION.minor == 2 include("reference.jl") else @info "Skipping reference tests" julia=VERSION end - finally - popfirst!(DEPOT_PATH) end + finally + popfirst!(DEPOT_PATH) end end diff --git a/test/template.jl b/test/template.jl index e23014b..8ddf5d7 100644 --- a/test/template.jl +++ b/test/template.jl @@ -1,52 +1,77 @@ -@testset "Template constructor" begin - @testset "user" begin - if isempty(PT.default_user()) - @test_throws ArgumentError Template() - haskey(ENV, "CI") && run(`git config --global github.user $USER`) - end - @test Template().user == PT.default_user() - end - - @testset "authors" begin - @test tpl(; authors=["a"]).authors == ["a"] - @test tpl(; authors="a").authors == ["a"] - @test tpl(; authors="a,b").authors == ["a", "b"] - @test tpl(; authors="a, b").authors == ["a", "b"] - end - - @testset "host" begin - @test tpl(; host="https://foo.com").host == "foo.com" - end - - @testset "dir" begin - @test tpl(; dir="/foo/bar").dir == joinpath(path_separator, "foo", "bar") - @test tpl(; dir="foo").dir == abspath("foo") - @test tpl(; dir="~/foo").dir == abspath(expanduser("~/foo")) - end - - @testset "plugins / disabled_defaults" begin - function test_plugins(plugins, expected, disabled=DataType[]) - t = tpl(; plugins=plugins, disable_defaults=disabled) - @test issetequal(values(t.plugins), expected) +@testset "Template" begin + @testset "Template constructor" begin + @testset "user" begin + if isempty(PT.default_user()) + @test_throws ArgumentError Template() + haskey(ENV, "CI") && run(`git config --global github.user $USER`) + end + @test Template().user == PT.default_user() end - defaults = PT.default_plugins() - test_plugins([], defaults) - test_plugins([Citation()], union(defaults, [Citation()])) - # Overriding a default plugin. - gi = Gitignore(; dev=false) - test_plugins([gi], union(setdiff(defaults, [Gitignore()]), [gi])) - # Disabling a default plugin. - test_plugins([], setdiff(defaults, [Gitignore()]), [Gitignore]) + @testset "authors" begin + @test tpl(; authors=["a"]).authors == ["a"] + @test tpl(; authors="a").authors == ["a"] + @test tpl(; authors="a,b").authors == ["a", "b"] + @test tpl(; authors="a, b").authors == ["a", "b"] + end + + @testset "host" begin + @test tpl(; host="https://foo.com").host == "foo.com" + end + + @testset "dir" begin + @test tpl(; dir="/foo/bar").dir == joinpath(path_separator, "foo", "bar") + @test tpl(; dir="foo").dir == abspath("foo") + @test tpl(; dir="~/foo").dir == abspath(expanduser("~/foo")) + end + + @testset "plugins / disabled_defaults" begin + function test_plugins(plugins, expected, disabled=DataType[]) + t = tpl(; plugins=plugins, disable_defaults=disabled) + @test issetequal(values(t.plugins), expected) + end + + defaults = PT.default_plugins() + test_plugins([], defaults) + test_plugins([Citation()], union(defaults, [Citation()])) + # Overriding a default plugin. + gi = Gitignore(; dev=false) + test_plugins([gi], union(setdiff(defaults, [Gitignore()]), [gi])) + # Disabling a default plugin. + test_plugins([], setdiff(defaults, [Gitignore()]), [Gitignore]) + end + end + + @testset "hasplugin" begin + t = tpl(; plugins=[Documenter{TravisCI}()]) + @test PT.hasplugin(t, typeof(first(PT.default_plugins()))) + @test PT.hasplugin(t, Documenter) + @test PT.hasplugin(t, _ -> true) + @test !PT.hasplugin(t, _ -> false) + @test !PT.hasplugin(t, Citation) + @test !PT.hasplugin(t, PT.is_ci) end end -@testset "hasplugin" begin - t = tpl(; plugins=[Documenter{TravisCI}()]) - @test PT.hasplugin(t, typeof(first(PT.default_plugins()))) - @test PT.hasplugin(t, Documenter) - @test PT.hasplugin(t, _ -> true) - @test !PT.hasplugin(t, _ -> false) - @test !PT.hasplugin(t, Citation) - @test !PT.hasplugin(t, PT.is_ci) +@context ErrorOnRepoInit +function Cassette.prehook(::ErrorOnRepoInit, ::typeof(LibGit2.init), pkg_dir) + @test isdir(pkg_dir) + error() +end + +@testset "Package generation errors" begin + mktempdir() do dir + t = tpl(; dir=dirname(dir)) + @test_throws ArgumentError t(basename(dir)) + end + + mktemp() do f, _io + t = tpl(; dir=dirname(f)) + @test_throws ArgumentError t(basename(f)) + end + + t = tpl() + pkg = pkgname() + @test_throws ErrorException @overdub ErrorOnRepoInit() @suppress t(pkg) + @test !isdir(joinpath(t.dir, pkg)) end