diff --git a/.appveyor.yml b/.appveyor.yml index 5abf5128..11ec9322 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,9 +20,6 @@ build_script: - echo "%JL_BUILD_SCRIPT%" - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" test_script: - # Git configuration is required to make commits in generated packages. - - git config --global user.name "AppVeyor" - - git config --global user.email "appveyor@example.com" - echo "%JL_TEST_SCRIPT%" - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" on_success: diff --git a/.travis.yml b/.travis.yml index 56152214..c7fbcda5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,10 @@ matrix: fast_finish: true notifications: email: false -before_script: - # Git configuration is required to make commits in generated packages. - - git config --global user.name "Travis" - - git config --global user.email "travis@example.com" after_success: - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; CodeCov.submit(process_folder())' - # For zero-argument template example. + # For package generation examples. + - git config --global user.name "Travis" + - git config --global user.email "travis@c.i" - git config --global github.user "travis" - julia -e 'using Pkg; ps=Pkg.PackageSpec(name="Documenter", version="0.19"); Pkg.add(ps); Pkg.pin(ps); include(joinpath("docs", "make.jl"))' diff --git a/Manifest.toml b/Manifest.toml index 8271d25b..de9ec39a 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -37,9 +37,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[Mustache]] deps = ["Pkg", "Tables", "Test"] -git-tree-sha1 = "455807b7c098d8a31f26792f685d5be250e83292" +git-tree-sha1 = "1cee2f530502aa2357724e7b19af3239b2e7f6b7" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "0.5.4" +version = "0.5.5" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] diff --git a/Project.toml b/Project.toml index ac09fd95..15d03f9a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PkgTemplates" uuid = "19f0ff7e-bab4-11e8-074b-97459630f98a" authors = ["Chris de Graaf "] -version = "0.1.0" +version = "0.3.0" [deps] AutoHashEquals = "15f4f7f2-30c1-5605-9d31-71845cf9641f" @@ -12,5 +12,10 @@ LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" URIParser = "30578b45-9adc-5946-b283-645ec420af67" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/src/generate.jl b/src/generate.jl index 0c64bb5b..5b77f381 100644 --- a/src/generate.jl +++ b/src/generate.jl @@ -1,27 +1,40 @@ """ - generate(pkg_name::AbstractString, t::Template) -> Nothing + generate(pkg::AbstractString, t::Template) -> Nothing + generate(t::Template, pkg::AbstractString) -> Nothing -Generate a package named `pkg_name` from `t`. +Generate a package named `pkg` from `t`. """ -function generate(pkg_name::AbstractString, t::Template) - pkg_dir = joinpath(t.dir, pkg_name) +function generate( + pkg::AbstractString, + t::Template; + gitconfig::Union{GitConfig, Nothing}=nothing, +) + pkg = splitjl(pkg) + pkg_dir = joinpath(t.dir, pkg) ispath(pkg_dir) && throw(ArgumentError("$pkg_dir already exists")) try - pkg_name = splitjl(pkg_name) - pkg_dir = joinpath(t.dir, pkg_name) - # Create the directory with some boilerplate inside. Pkg.generate(pkg_dir) # Initialize the repo. repo = LibGit2.init(pkg_dir) @info "Initialized git repo at $pkg_dir" + + if gitconfig !== nothing + # Configure the repo. + repoconfig = GitConfig(repo) + for c in LibGit2.GitConfigIter(gitconfig) + LibGit2.set!(repoconfig, unsafe_string(c.name), unsafe_string(c.value)) + end + end + + # Commit and set the remote. LibGit2.commit(repo, "Initial commit") rmt = if t.ssh - "git@$(t.host):$(t.user)/$pkg_name.jl.git" + "git@$(t.host):$(t.user)/$pkg.jl.git" else - "https://$(t.host)/$(t.user)/$pkg_name.jl" + "https://$(t.host)/$(t.user)/$pkg.jl" end # We need to set the remote in a strange way, see #8. close(LibGit2.GitRemote(repo, "origin", rmt)) @@ -30,7 +43,7 @@ function generate(pkg_name::AbstractString, t::Template) # Create the gh-pages branch if necessary. if haskey(t.plugins, GitHubPages) LibGit2.branch!(repo, "gh-pages") - LibGit2.commit(repo, "Empty initial commit") + LibGit2.commit(repo, "Initial commit") @info "Created empty gh-pages branch" LibGit2.branch!(repo, "master") end @@ -43,14 +56,13 @@ function generate(pkg_name::AbstractString, t::Template) gen_readme(pkg_dir, t), gen_gitignore(pkg_dir, t), gen_license(pkg_dir, t), - vcat(map(p -> gen_plugin(p, t, pkg_name), values(t.plugins))...), + vcat(map(p -> gen_plugin(p, t, pkg), values(t.plugins))...), ) LibGit2.add!(repo, files...) LibGit2.commit(repo, "Files generated by PkgTemplates") - @info "Staged and committed $(length(files)) files/directories: $(join(files, ", "))" + @info "Committed $(length(files)) files/directories: $(join(files, ", "))" - @info "Finished" if length(collect(LibGit2.GitBranchIter(repo))) > 1 @info "Remember to push all created branches to your remote: git push --all" end @@ -60,7 +72,13 @@ function generate(pkg_name::AbstractString, t::Template) end end -generate(t::Template, pkg_name::AbstractString) = generate(pkg_name, t) +function generate( + t::Template, + pkg::AbstractString; + gitconfig::Union{GitConfig, Nothing}=nothing, +) + generate(pkg, t; gitconfig=gitconfig) +end """ generate_interactive(pkg::AbstractString; fast::Bool=false) -> Template @@ -69,9 +87,13 @@ Interactively create a template, and then generate a package with it. Arguments keywords are used in the same way as in [`generate`](@ref) and [`interactive_template`](@ref). """ -function generate_interactive(pkg::AbstractString; fast::Bool=false) +function generate_interactive( + pkg::AbstractString; + fast::Bool=false, + gitconfig::Union{GitConfig, Nothing}=nothing, +) t = interactive_template(; fast=fast) - generate(pkg, t) + generate(pkg, t; gitconfig=gitconfig) return t end @@ -87,13 +109,28 @@ Create the test entrypoint in `pkg_dir`. Returns an array of generated file/directory names. """ function gen_tests(pkg_dir::AbstractString, t::Template) + # TODO: Silence Pkg for this section? Adding and removing Test creates a lot of noise. proj = Base.current_project() try Pkg.activate(pkg_dir) Pkg.add("Test") + + # Move the Test dependency into the [extras] section. + toml = read(joinpath(pkg_dir, "Project.toml"), String) + lines = split(toml, "\n") + idx = findfirst(l -> startswith(l, "Test = "), lines) + testdep = lines[idx] + deleteat!(lines, idx) + toml = join(lines, "\n") * """ + [extras] + $testdep + + [targets] + test = ["Test"] + """ + gen_file(joinpath(pkg_dir, "Project.toml"), toml) + Pkg.update() # Regenerate Manifest.toml (this cleans up Project.toml too). finally - # TODO: What should we do if there is no current project? - # Activating the generated project is now a side effect. proj !== nothing && Pkg.activate(dirname(proj)) end @@ -104,12 +141,10 @@ function gen_tests(pkg_dir::AbstractString, t::Template) @testset "$pkg.jl" begin # Write your own tests here. - @test 1 == 2 end """ gen_file(joinpath(pkg_dir, "test", "runtests.jl"), text) - # TODO: Should we be checking Manifest.toml into Git? return ["Manifest.toml", "test/"] end @@ -298,9 +333,4 @@ function substitute( return substitute(template, merge(d, view)) end -""" - splitjl(pkg::AbstractString) -> AbstractString - -Remove ".jl" from the end of a package name if it is present. -""" splitjl(pkg::AbstractString) = endswith(pkg, ".jl") ? pkg[1:end-3] : pkg diff --git a/src/template.jl b/src/template.jl index 78d3a044..22a8c749 100644 --- a/src/template.jl +++ b/src/template.jl @@ -1,10 +1,3 @@ -""" - dev_dir() -> String - -Get the default development directory (~/.julia/dev). -""" -dev_dir() = joinpath(first(DEPOT_PATH), "dev") - """ Template(; kwargs...) -> Template @@ -13,9 +6,9 @@ create a template, you can use [`interactive_template`](@ref) instead. # Keyword Arguments * `user::AbstractString=""`: GitHub (or other code hosting service) username. If left - unset, it will take the the global git config's value. If that is not set, an - `ArgumentError` is thrown. **This is case-sensitive for some plugins, so take care to - enter it correctly.** + 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.** * `host::AbstractString="github.com"`: URL to the code hosting service where your package 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. @@ -26,9 +19,9 @@ create a template, you can use [`interactive_template`](@ref) instead. * `authors::Union{AbstractString, Vector{<:AbstractString}}=""`: Names that appear on the license. Supply a string for one author or an array for multiple. Similarly to `user`, it will take the value of of the global git config's value if it is left unset. -* `dir::AbstractString=$(dev_dir())`: 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. +* `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. * `ssh::Bool=false`: Whether or not to use SSH for the remote. * `plugins::Vector{<:Plugin}=Plugin[]`: A list of `Plugin`s that the package will include. """ @@ -47,22 +40,18 @@ create a template, you can use [`interactive_template`](@ref) instead. host::AbstractString="https://github.com", license::AbstractString="MIT", authors::Union{AbstractString, Vector{<:AbstractString}}="", - dir::AbstractString=dev_dir(), + dir::AbstractString=Pkg.devdir(), julia_version::VersionNumber=VERSION, ssh::Bool=false, plugins::Vector{<:Plugin}=Plugin[], ) # Check for required Git options for package generation # (you can't commit to a repository without them). - if isempty(LibGit2.getconfig("user.name", "")) - @warn "Git config option 'user.name' missing, package generation will fail" - end - if isempty(LibGit2.getconfig("user.email", "")) - @warn "Git config option 'user.email' missing, package generation will fail" - end + isempty(LibGit2.getconfig("user.name", "")) && missingopt("user.name") + isempty(LibGit2.getconfig("user.email", "")) && missingopt("user.email") # If no username was set, look for one in the global git config. - # Note: This is one of a few GitHub specifics. + # Note: This is one of a few GitHub specifics (maybe we could use the host value). if isempty(user) user = LibGit2.getconfig("github.user", "") end @@ -186,9 +175,9 @@ function interactive_template(; fast::Bool=false) end kwargs[:dir] = if fast - dev_dir() + Pkg.devdir() else - default_dir = dev_dir() + default_dir = Pkg.devdir() print("Enter the path to the package directory [$default_dir]: ") dir = readline() isempty(dir) ? default_dir : dir @@ -223,9 +212,6 @@ function interactive_template(; fast::Bool=false) return Template(; kwargs...) end -""" - leaves(t:Type) -> Vector{DataType} - -Get all concrete subtypes of `t`. -""" leaves(t::Type)::Vector{DataType} = isconcretetype(t) ? [t] : vcat(leaves.(subtypes(t))...) + +missingopt(name) = @warn "Git config option '$name' missing, package generation will fail unless you supply a GitConfig" diff --git a/test/gitconfig b/test/gitconfig new file mode 100644 index 00000000..8675f326 --- /dev/null +++ b/test/gitconfig @@ -0,0 +1,3 @@ +[user] + name = Travis + email = travis@c.i diff --git a/test/interactive/interactive.jl b/test/interactive/interactive.jl index 23c5598d..e0a121da 100644 --- a/test/interactive/interactive.jl +++ b/test/interactive/interactive.jl @@ -52,7 +52,7 @@ end @testset "Interactive package generation" begin write(stdin.buffer, "$me\n\n\r\n\n\n\n\n\nd") - generate_interactive(test_pkg) + generate_interactive(test_pkg; gitconfig=gitconfig) @test isdir(joinpath(default_dir, test_pkg)) rm(joinpath(default_dir, test_pkg); force=true, recursive=true) end diff --git a/test/plugins/githubpages.jl b/test/plugins/githubpages.jl index 9681b742..ba1104e5 100644 --- a/test/plugins/githubpages.jl +++ b/test/plugins/githubpages.jl @@ -56,7 +56,7 @@ pkg_dir = joinpath(t.dir, test_pkg) @testset "Package generation with GitHubPages plugin" begin temp_dir = mktempdir() t = Template(; user=me, dir=temp_dir, plugins=[GitHubPages()]) - generate(test_pkg, t) + generate(test_pkg, t; gitconfig=gitconfig) # Check that the gh-pages branch exists. repo = LibGit2.GitRepo(joinpath(t.dir, test_pkg)) diff --git a/test/runtests.jl b/test/runtests.jl index e1abc324..8bcd9a8f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,7 @@ using PkgTemplates using Test using Dates using LibGit2 +using Pkg import PkgTemplates: badges, version_floor, substitute, read_license, gen_file, gen_readme, gen_tests, gen_license, gen_require, gen_gitignore, gen_plugin, show_license, LICENSES, diff --git a/test/tests.jl b/test/tests.jl index 812b6dbd..be1a9aef 100644 --- a/test/tests.jl +++ b/test/tests.jl @@ -19,7 +19,8 @@ const me = "christopher-dG" const test_pkg = "TestPkg" const fake_path = "/dev/null/this/file/does/not/exist" const test_file = tempname() -const default_dir = PkgTemplates.dev_dir() +const default_dir = Pkg.devdir() +const gitconfig = GitConfig(joinpath(@__DIR__, "gitconfig")) const template_text = """ PKGNAME: {{PKGNAME}} VERSION: {{VERSION}}} @@ -79,7 +80,7 @@ write(test_file, template_text) ) # Duplicate plugins should warn. - @test_logs (:warn, r".+") t = Template(; + @test_logs (:warn, r"duplicates") match_mode=:any t = Template(; user=me, plugins=[TravisCI(), TravisCI()], ) @@ -224,6 +225,9 @@ end # Test the test generation. @test gen_tests(pkg_dir, t) == ["Manifest.toml", "test/"] + @test isfile(joinpath(pkg_dir, "Project.toml")) + project = read(joinpath(pkg_dir, "Project.toml"), String) + @test occursin("[extras]\nTest = ", project) @test isdir(joinpath(pkg_dir, "test")) @test isfile(joinpath(pkg_dir, "test", "runtests.jl")) @test isfile(joinpath(pkg_dir, "Manifest.toml")) @@ -232,14 +236,14 @@ end @test occursin("using $test_pkg", runtests) @test occursin("using Test", runtests) manifest = read(joinpath(pkg_dir, "Manifest.toml"), String) - @test occursin("[[Test]]", manifest) + @test !occursin("[[Test]]", manifest) rm(temp_dir; recursive=true) end @testset "Package generation" begin t = Template(; user=me) - generate(test_pkg, t) + generate(test_pkg, t; gitconfig=gitconfig) pkg_dir = joinpath(default_dir, test_pkg) # Check that the expected files all exist. @@ -266,7 +270,7 @@ end # Check that the remote is an SSH URL when we want it to be. t = Template(; user=me, ssh=true) - generate(t, test_pkg) # Test the reversed-arguments method here. + generate(t, test_pkg; gitconfig=gitconfig) # Test the reversed-arguments method here. repo = LibGit2.GitRepo(pkg_dir) remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") @test LibGit2.url(remote) == "git@github.com:$me/$test_pkg.jl.git" @@ -274,7 +278,7 @@ end # Check that the remote is set correctly for non-default hosts. t = Template(; user=me, host="gitlab.com") - generate(test_pkg, t) + generate(test_pkg, t; gitconfig=gitconfig) repo = LibGit2.GitRepo(pkg_dir) remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") @test LibGit2.url(remote) == "https://gitlab.com/$me/$test_pkg.jl" @@ -283,7 +287,7 @@ end # Check that the package ends up in the right directory. temp_dir = mktempdir() t = Template(; user=me, dir=temp_dir) - generate(test_pkg, t) + generate(test_pkg, t; gitconfig=gitconfig) @test isdir(joinpath(temp_dir, test_pkg)) rm(temp_dir; recursive=true) end