diff --git a/.travis.yml b/.travis.yml index 89a1971..a9afd3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ before_script: - git config --global user.name "Travis" - git config --global user.email "travis@example.com" after_success: - - julia -e 'using Pkg; Pkg.add("Coverage")' - - julia -e 'using Coverage; CodeCov.submit(process_folder())' + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; CodeCov.submit(process_folder())' + # For zero-argument template example. + - git config --global github.user "travis" - julia -e 'using Pkg; Pkg.add("Documenter"); include(joinpath("docs", "make.jl"))' diff --git a/Manifest.toml b/Manifest.toml index c51e0e9..8271d25 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -36,10 +36,10 @@ deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[Mustache]] -deps = ["Pkg", "Test"] -git-tree-sha1 = "fb4b57a278d18434eff78bdc2c06238f6ee3c9e7" +deps = ["Pkg", "Tables", "Test"] +git-tree-sha1 = "455807b7c098d8a31f26792f685d5be250e83292" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "0.5.1" +version = "0.5.4" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] @@ -57,6 +57,12 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +[[Requires]] +deps = ["Test"] +git-tree-sha1 = "f6fbf4ba64d295e146e49e021207993b6b48c7d1" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "0.5.2" + [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -66,6 +72,12 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[Tables]] +deps = ["Pkg", "Requires", "Test"] +git-tree-sha1 = "7b7e95a34c2c2504f1403aa9005399e239f461b9" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "0.1.9" + [[Test]] deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/Project.toml b/Project.toml index a238339..ac09fd9 100644 --- a/Project.toml +++ b/Project.toml @@ -10,6 +10,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" 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" diff --git a/README.md b/README.md index 1ecb293..203d106 100644 --- a/README.md +++ b/README.md @@ -11,50 +11,73 @@ repeatable, and customizable way.** ## Installation -`PkgTemplates` is registered in -[`METADATA.jl`](https://github.com/JuliaLang/METADATA.jl), so run -`Pkg.add("PkgTemplates")` for the latest release, or -`Pkg.clone("PkgTemplates")` for the development version. +```julia +(v1.0) pkg> add PkgTemplates +``` ## Usage -The simplest template only requires your GitHub username. +The simplest template requires no arguments. ```julia julia> using PkgTemplates -julia> t = Template(; user="myusername"); +julia> t = Template() +Template: + → User: christopher-dG + → Host: github.com + → License: MIT (Chris de Graaf 2018) + → Package directory: ~/.julia/dev + → Minimum Julia version: v1.0 + → SSH remote: No + → Plugins: None julia> generate("MyPkg", t) -INFO: Initialized git repo at /tmp/tmpvaHVki/MyPkg -INFO: Made empty initial commit -INFO: Set remote origin to https://github.com/myusername/MyPkg.jl -INFO: Staged 6 files/directories: src/, test/, REQUIRE, README.md, .gitignore, LICENSE -INFO: Committed files generated by PkgTemplates -INFO: Moved temporary package directory into /home/degraafc/.julia/v0.6/ -INFO: Finished +Generating project MyPkg: + /Users/degraafc/.julia/dev/MyPkg/Project.toml + /Users/degraafc/.julia/dev/MyPkg/src/MyPkg.jl +[ Info: Initialized git repo at /Users/degraafc/.julia/dev/MyPkg +[ Info: Set remote origin to https://github.com/myusername/MyPkg.jl + Updating registry at `~/.julia/registries/General` + Updating git-repo `https://github.com/JuliaRegistries/General.git` + Resolving package versions... + Updating `~/.julia/dev/MyPkg/Project.toml` + [8dfed614] + Test + Updating `~/.julia/dev/MyPkg/Manifest.toml` + [2a0f44e3] + Base64 + [8ba89e20] + Distributed + [b77e0a4c] + InteractiveUtils + [8f399da3] + Libdl + [37e2e46d] + LinearAlgebra + [56ddb016] + Logging + [d6f4376e] + Markdown + [9a3f8284] + Random + [9e88b42a] + Serialization + [6462fe0b] + Sockets + [8dfed614] + Test +[ Info: Staged and committed 8 files/directories: src/, Project.toml, Manifest.toml, test/, REQUIRE, README.md, .gitignore, LICENSE +[ Info: Finished -julia> run(`git -C $(joinpath(t.dir, "MyPkg")) ls-tree -r --name-only HEAD`) +julia> run(`git -C $(joinpath(t.dir, "MyPkg")) ls-files`); .gitignore LICENSE +Manifest.toml +Project.toml README.md REQUIRE src/MyPkg.jl test/runtests.jl ``` -However, we can also configure a number of keyword arguments to `Template` and -`generate`: + +However, we can also configure a number of keyword arguments to `Template`: ```julia julia> t = Template(; user="myusername", license="ISC", authors=["Chris de Graaf", "Invenia Technical Computing Corporation"], - years="2016-2017", dir=joinpath(homedir(), "code"), - julia_version=v"0.5.2", - requirements=["PkgTemplates"], - gitconfig=Dict("diff.renames" => true), + julia_version=v"0.7", plugins=[ TravisCI(), CodeCov(), @@ -62,30 +85,69 @@ julia> t = Template(; AppVeyor(), GitHubPages(), ], - ); + ) +Template: + → User: myusername + → Host: github.com + → License: ISC (Chris de Graaf, Invenia Technical Computing Corporation 2018) + → Package directory: ~/code + → Minimum Julia version: v0.7 + → SSH remote: No + → Plugins: + • AppVeyor: + → Config file: Default + → 0 gitignore entries + • CodeCov: + → Config file: None + → 3 gitignore entries: "*.jl.cov", "*.jl.*.cov", "*.jl.mem" + • Coveralls: + → Config file: None + → 3 gitignore entries: "*.jl.cov", "*.jl.*.cov", "*.jl.mem" + • GitHubPages: + → 0 asset files + → 2 gitignore entries: "/docs/build/", "/docs/site/" + • TravisCI: + → Config file: Default + → 0 gitignore entries -julia> generate("MyPkg", t; force=true, ssh=true) -INFO: Initialized git repo at /tmp/tmpe0dWY5/MyPkg -INFO: Applied git configuration -INFO: Made empty initial commit -INFO: Set remote origin to git@github.com:myusername/MyPkg.jl.git -INFO: Created empty gh-pages branch -INFO: Staged 9 files/directories: src/, test/, REQUIRE, README.md, .gitignore, LICENSE, docs/, .appveyor.yml, .travis.yml -INFO: Committed files generated by PkgTemplates -INFO: Moved temporary package directory into /home/degraafc/code/ -INFO: Finished -WARNING: Remember to push all created branches to your remote: git push --all +julia> generate(t, "MyPkg2") +Generating project MyPkg2: + /Users/degraafc/code/MyPkg2/Project.toml + /Users/degraafc/code/MyPkg2/src/MyPkg2.jl +[ Info: Initialized git repo at /Users/degraafc/code/MyPkg2 +[ Info: Set remote origin to https://github.com/myusername/MyPkg2.jl +[ Info: Created empty gh-pages branch + Resolving package versions... + Updating `~/code/MyPkg2/Project.toml` + [8dfed614] + Test + Updating `~/code/MyPkg2/Manifest.toml` + [2a0f44e3] + Base64 + [8ba89e20] + Distributed + [b77e0a4c] + InteractiveUtils + [8f399da3] + Libdl + [37e2e46d] + LinearAlgebra + [56ddb016] + Logging + [d6f4376e] + Markdown + [9a3f8284] + Random + [9e88b42a] + Serialization + [6462fe0b] + Sockets + [8dfed614] + Test +[ Info: Staged and committed 11 files/directories: src/, Project.toml, Manifest.toml, test/, REQUIRE, README.md, .gitignore, LICENSE, .appveyor.yml, .travis.yml, docs/ +[ Info: Finished +[ Info: Remember to push all created branches to your remote: git push --all -julia> run(`git -C $(joinpath(t.dir, "MyPkg")) ls-tree -r --name-only HEAD`) +julia> run(`git -C $(joinpath(t.dir, "MyPkg2")) ls-files`); .appveyor.yml .gitignore .travis.yml LICENSE +Manifest.toml +Project.toml README.md REQUIRE docs/make.jl docs/src/index.md -src/MyPkg.jl +src/MyPkg2.jl test/runtests.jl ``` @@ -95,7 +157,7 @@ Information on each keyword as well as plugin types can be found in the If that looks like a lot of work, you can also create templates interactively with `interactive_template`: -[![asciicast](https://asciinema.org/a/bqBwff05mI7Cl9bz7EqLPMKF8.png)](https://asciinema.org/a/bqBwff05mI7Cl9bz7EqLPMKF8) +[![asciicast](https://asciinema.org/a/31bZqW9u8h5RHpd7gtsemioRV.png)](https://asciinema.org/a/31bZqW9u8h5RHpd7gtsemioRV) And if that's **still** too much work for you, you can call `interactive_template` with `fast=true` to use default values for everything diff --git a/defaults/appveyor.yml b/defaults/appveyor.yml index 6ae2557..c9f4dec 100644 --- a/defaults/appveyor.yml +++ b/defaults/appveyor.yml @@ -1,49 +1,33 @@ +# Documentation: https://github.com/JuliaCI/Appveyor.jl environment: matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/{{VERSION}}/julia-{{VERSION}}-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/{{VERSION}}/julia-{{VERSION}}-latest-win64.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - + - julia_version: {{VERSION}} + - julia_version: nightly +platform: + - x86 + - x64 matrix: allow_failures: - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - + - julia_version: nightly branches: only: - master - /release-.*/ - notifications: - provider: Email on_build_success: false on_build_failure: false on_build_status_changed: false - install: - - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" - # Download most recent Julia Windows binary - - ps: (new-object net.webclient).DownloadFile( - $env:JULIA_URL, - "C:\projects\julia-binary.exe") - # Run installer silently, output to C:\projects\julia - - C:\projects\julia-binary.exe /S /D=C:\projects\julia - + - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) build_script: - # Need to convert from shallow to complete for Pkg.clone to work - - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); Pkg.clone(pwd(), \"{{PKGNAME}}\"); Pkg.build(\"{{PKGNAME}}\")" - + - echo "%JL_BUILD_SCRIPT%" + - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" test_script: - - C:\projects\julia\bin\julia -e "Pkg.test(\"{{PKGNAME}}\"{{#AFTER}}; coverage=true{{/AFTER}})" -{{#AFTER}} - -after_test: + - echo "%JL_TEST_SCRIPT%" + - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" {{#CODECOV}} - - C:\projects\julia\bin\julia -e "cd(Pkg.dir(\"{{PKGNAME}}\")); Pkg.add(\"Coverage\"); using Coverage; Codecov.submit(process_folder())" +on_success: + - echo "%JL_CODECOV_SCRIPT%" + - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" {{/CODECOV}} -{{#COVERALLS}} - - C:\projects\julia\bin\julia -e "cd(Pkg.dir(\"{{PKGNAME}}\")); Pkg.add(\"Coverage\"); using Coverage; Coveralls.submit(process_folder())" -{{/COVERALLS}} -{{/AFTER}} diff --git a/defaults/gitlab-ci.yml b/defaults/gitlab-ci.yml index 49f1693..35f9ffe 100644 --- a/defaults/gitlab-ci.yml +++ b/defaults/gitlab-ci.yml @@ -1,45 +1,8 @@ -{{#GITLABCOVERAGE}} -stages: - - test - - coverage - -{{/GITLABCOVERAGE}} -.test_template: &test_template - stage: test -{{#GITLABCOVERAGE}} - artifacts: - name: coverage - expire_in: 2 hours - paths: - - coverage/ -{{/GITLABCOVERAGE}} - tags: - - docker - script: - - julia -e 'Pkg.clone(pwd()); Pkg.build("{{PKGNAME}}"); Pkg.test("{{PKGNAME}}"{{#GITLABCOVERAGE}}; coverage=true{{/GITLABCOVERAGE}})' -{{#GITLABCOVERAGE}} - - cp -r $(julia -e 'print(Pkg.dir("{{PKGNAME}}", "src"))') coverage -{{/GITLABCOVERAGE}} - Julia {{VERSION}}: image: julia:{{VERSION}} - <<: *test_template - -Julia nightly: - image: staticfloat/julia:nightly-x64 - allow_failure: true - <<: *test_template + script: julia --project='@.' -e 'using Pkg; Pkg.build(); Pkg.test({{#GITLABCOVERAGE}}; coverage=true{{/GITLABCOVERAGE}})' {{#GITLABCOVERAGE}} - -"Coverage": - stage: coverage coverage: /Test Coverage (\d+\.\d+%)/ - image: julia:{{VERSION}} - tags: - - docker - before_script: - - apt-get update && apt-get -y install git make unzip gcc bzip2 - script: - - rm -rf src && mv coverage src - - julia -e 'Pkg.add("Coverage"); using Coverage; c, t = get_summary(process_folder()); @printf("Test Coverage %.2f%%\n", 100c/t)' + after_script: + - julia -e 'using Printf; using Pkg; Pkg.add("Coverage"); using Coverage; c, t = get_summary(process_folder()); @printf "Test Coverage %.2f%%\n" 100c/t' {{/GITLABCOVERAGE}} diff --git a/defaults/travis.yml b/defaults/travis.yml index 7c4b7eb..62767c7 100644 --- a/defaults/travis.yml +++ b/defaults/travis.yml @@ -12,18 +12,15 @@ matrix: fast_finish: true notifications: email: false -script: - - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi - - julia -e 'Pkg.clone(pwd()); Pkg.build("{{PKGNAME}}"); Pkg.test("{{PKGNAME}}"{{#AFTER}}; coverage=true{{/AFTER}})' {{#AFTER}} after_success: {{#CODECOV}} - - julia -e 'cd(Pkg.dir("{{PKGNAME}}")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' {{/CODECOV}} {{#COVERALLS}} - - julia -e 'cd(Pkg.dir("{{PKGNAME}}")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' + - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' {{/COVERALLS}} {{#DOCUMENTER}} - - julia -e 'Pkg.add("Documenter"); cd(Pkg.dir("{{PKGNAME}}")); include(joinpath("docs", "make.jl"))' + - julia -e 'using Pkg; Pkg.add("Documenter"); include(joinpath("docs", "make.jl"))' {{/DOCUMENTER}} {{/AFTER}} diff --git a/docs/make.jl b/docs/make.jl index a79546c..ccb28ee 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -20,7 +20,7 @@ makedocs(; deploydocs(; repo="github.com/invenia/PkgTemplates.jl", target="build", - julia="0.6", + julia="1.0", deps=nothing, make=nothing, ) diff --git a/docs/src/index.md b/docs/src/index.md index 7314e17..99077e6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -11,24 +11,23 @@ repeatable, and customizable way.** ## Installation -`PkgTemplates` is registered in -[`METADATA.jl`](https://github.com/JuliaLang/METADATA.jl), so run -`Pkg.add("PkgTemplates")` for the latest release, or -`Pkg.clone("PkgTemplates")` for the development version. +```julia +(v1.0) pkg> add PkgTemplates +``` ## Usage -The simplest template only requires your GitHub username. +The simplest template requires no arguments. ```@repl using PkgTemplates -t = Template(; user="myusername"); +t = Template() generate("MyPkg", t) -run(`git -C $(joinpath(t.dir, "MyPkg")) ls-tree -r --name-only HEAD`) +run(`git -C $(joinpath(t.dir, "MyPkg")) ls-files`); ``` However, we can also configure a number of keyword arguments to -[`Template`](@ref) and [`generate`](@ref): +[`Template`](@ref): ```@repl using PkgTemplates @@ -36,11 +35,9 @@ t = Template(; user="myusername", license="MIT", authors=["Chris de Graaf", "Invenia Technical Computing Corporation"], - years="2016-2017", dir=joinpath(homedir(), "code"), - julia_version=v"0.5.2", - requirements=["PkgTemplates"], - gitconfig=Dict("diff.renames" => true), + julia_version=v"0.7", + ssh=true, plugins=[ TravisCI(), CodeCov(), @@ -48,15 +45,15 @@ t = Template(; AppVeyor(), GitHubPages(), ], -); -generate("MyPkg", t; force=true, ssh=true) -run(`git -C $(joinpath(t.dir, "MyPkg")) ls-tree -r --name-only HEAD`) +) +generate("MyPkg2", t) +run(`git -C $(joinpath(t.dir, "MyPkg2")) ls-tree -r --name-only HEAD`); ``` If that looks like a lot of work, you can also create templates interactively with [`interactive_template`](@ref): -[![asciicast](https://asciinema.org/a/bqBwff05mI7Cl9bz7EqLPMKF8.png)](https://asciinema.org/a/bqBwff05mI7Cl9bz7EqLPMKF8) +[![asciicast](https://asciinema.org/a/31bZqW9u8h5RHpd7gtsemioRV.png)](https://asciinema.org/a/31bZqW9u8h5RHpd7gtsemioRV) And if that's **still** too much work for you, you can call `interactive_template` with `fast=true` to use default values for everything @@ -65,10 +62,16 @@ but username and plugin selection. You can also use [`generate_interactive`](@ref) to interactively generate a template and then immediately use it to create a new package. -## Comparison to [PkgDev](https://github.com/JuliaLang/PkgDev.jl) +## Comparison to PkgDev -`PkgTemplates` is similar in functionality to `PkgDev`'s `generate` function. -However, `PkgTemplates` offers more customizability in templates and more -extensibility via plugins. For the package registration and release management -features that `PkgTemplates` lacks, you are encouraged to use -[AttoBot](https://github.com/apps/attobot) instead. +`PkgTemplates` is similar in functionality to +[`PkgDev`](https://github.com/JuliaLang/PkgDev.jl)'s `generate` function. However, +`PkgTemplates` offers more customizability in templates and more extensibility via plugins. +For the package registration and release management features that `PkgTemplates` doesn't +include, you are encouraged to use [AttoBot](https://github.com/apps/attobot) instead. + +## Contributing + +It's extremely easy to extend `PkgTemplates` with new plugins. To get started, +check out the +[plugin development guide](https://invenia.github.io/PkgTemplates.jl/stable/pages/plugin_development.html). diff --git a/docs/src/pages/package_generation.md b/docs/src/pages/package_generation.md index 7696799..758baff 100644 --- a/docs/src/pages/package_generation.md +++ b/docs/src/pages/package_generation.md @@ -24,7 +24,6 @@ generate_interactive ### Helper Functions ```@docs -gen_entrypoint gen_tests gen_require gen_readme diff --git a/src/PkgTemplates.jl b/src/PkgTemplates.jl index 407d0f6..7fc68d8 100644 --- a/src/PkgTemplates.jl +++ b/src/PkgTemplates.jl @@ -1,4 +1,3 @@ -__precompile__() module PkgTemplates using AutoHashEquals @@ -7,6 +6,7 @@ using Distributed using InteractiveUtils using LibGit2 using Mustache +using Pkg using REPL.TerminalMenus using URIParser diff --git a/src/generate.jl b/src/generate.jl index a5c31b3..1243d62 100644 --- a/src/generate.jl +++ b/src/generate.jl @@ -1,313 +1,188 @@ """ - generate( - pkg_name::AbstractString, - t::Template; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", - ) -> Nothing + generate(pkg_name::AbstractString, t::Template) -> Nothing -Generate a package named `pkg_name` from `template`. - -# Keyword Arguments -* `force::Bool=false`: Whether or not to overwrite old packages with the same name. -* `ssh::Bool=false`: Whether or not to use SSH for the remote. -* `backup_dir::AbstractString=""`: Directory in which to store the generated package if - `t.dir` is not a valid directory. If left unset, a temporary directory will be created - (this keyword is mostly for internal usage). - -# Notes -The package is generated entirely in a temporary directory and only moved into -`joinpath(t.dir, pkg_name)` at the very end. In the case of an error, the temporary -directory will contain leftovers, but the destination directory will remain untouched -(this is especially helpful when `force=true`). +Generate a package named `pkg_name` from `t`. """ -function generate( - pkg_name::AbstractString, - t::Template; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", -) - mktempdir() do temp_dir - generate(pkg_name, t, temp_dir; force=force, ssh=ssh, backup_dir=backup_dir) - end -end - -function generate( - pkg_name::AbstractString, - t::Template, - dir::AbstractString; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", -) - pkg_name = splitjl(pkg_name) +function generate(pkg_name::AbstractString, t::Template) pkg_dir = joinpath(t.dir, pkg_name) - temp_pkg_dir = joinpath(dir, pkg_name) + ispath(pkg_dir) && throw(ArgumentError("$pkg_dir already exists")) - if !force && ispath(pkg_dir) - throw(ArgumentError( - "Path '$pkg_dir' already exists, use force=true to overwrite it." - )) - end - - # Initialize the repo and configure it. - repo = LibGit2.init(temp_pkg_dir) - @info "Initialized git repo at $temp_pkg_dir" - LibGit2.with(LibGit2.GitConfig, repo) do cfg - for (key, val) in t.gitconfig - LibGit2.set!(cfg, key, val) - end - end - !isempty(t.gitconfig) && @info "Applied git configuration" - LibGit2.commit(repo, "Initial commit") - @info "Made empty initial commit" - rmt = if ssh - "git@$(t.host):$(t.user)/$pkg_name.jl.git" - else - "https://$(t.host)/$(t.user)/$pkg_name.jl" - end - # We need to set the remote in a strange way, see #8. - close(LibGit2.GitRemote(repo, "origin", rmt)) - @info "Set remote origin to $rmt" - - # Create the gh-pages branch if necessary. - if haskey(t.plugins, GitHubPages) - LibGit2.branch!(repo, "gh-pages") - LibGit2.commit(repo, "Empty initial commit") - @info "Created empty gh-pages branch" - LibGit2.branch!(repo, "master") - end - - # Generate the files. - files = vcat( - gen_entrypoint(dir, pkg_name, t), - gen_tests(dir, pkg_name, t), - gen_require(dir, pkg_name, t), - gen_readme(dir, pkg_name, t), - gen_gitignore(dir, pkg_name, t), - gen_license(dir, pkg_name, t), - vcat(map(p -> gen_plugin(p, t, dir, pkg_name), values(t.plugins))...), - ) - - LibGit2.add!(repo, files...) - @info "Staged $(length(files)) files/directories: $(join(files, ", "))" - LibGit2.commit(repo, "Files generated by PkgTemplates") - @info "Committed files generated by PkgTemplates" - multiple_branches = length(collect(LibGit2.GitBranchIter(repo))) > 1 try - mkpath(dirname(pkg_dir)) - mv(temp_pkg_dir, pkg_dir; force=force) - @info "Moved temporary package directory into $(t.dir)/" - catch # Likely cause is that t.dir can't be created (is a file, etc.). - # We're just going to trust that backup_dir is a valid directory. - backup_dir = if isempty(backup_dir) - mktempdir() + 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" + LibGit2.commit(repo, "Initial commit") + rmt = if t.ssh + "git@$(t.host):$(t.user)/$pkg_name.jl.git" else - abspath(backup_dir) + "https://$(t.host)/$(t.user)/$pkg_name.jl" end - mkpath(backup_dir) - mv(temp_pkg_dir, joinpath(backup_dir, pkg_name)) - @warn "$pkg_name couldn't be moved into $pkg_dir, left package in $backup_dir" - end + # We need to set the remote in a strange way, see #8. + close(LibGit2.GitRemote(repo, "origin", rmt)) + @info "Set remote origin to $rmt" - @info "Finished" - if multiple_branches - @info "Remember to push all created branches to your remote: git push --all" + # Create the gh-pages branch if necessary. + if haskey(t.plugins, GitHubPages) + LibGit2.branch!(repo, "gh-pages") + LibGit2.commit(repo, "Empty initial commit") + @info "Created empty gh-pages branch" + LibGit2.branch!(repo, "master") + end + + # Generate the files. + files = vcat( + "src/", "Project.toml", # Created by Pkg.generate. + gen_tests(pkg_dir, t), + gen_require(pkg_dir, t), + 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))...), + ) + + LibGit2.add!(repo, files...) + LibGit2.commit(repo, "Files generated by PkgTemplates") + @info "Staged and 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 + catch e + rm(pkg_dir; recursive=true) + rethrow(e) end end -function generate( - t::Template, - pkg_name::AbstractString; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", -) - generate(pkg_name, t; force=force, ssh=ssh, backup_dir=backup_dir) -end +generate(t::Template, pkg_name::AbstractString) = generate(pkg_name, t) """ - generate_interactive( - pkg_name::AbstractString; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", - fast::Bool=false, - ) -> Nothing + generate_interactive(pkg::AbstractString; fast::Bool=false) -> Template Interactively create a template, and then generate a package with it. Arguments and keywords are used in the same way as in [`generate`](@ref) and [`interactive_template`](@ref). """ -function generate_interactive( - pkg_name::AbstractString; - force::Bool=false, - ssh::Bool=false, - backup_dir::AbstractString="", - fast::Bool=false, -) - generate( - pkg_name, - interactive_template(; fast=fast); - force=force, - ssh=ssh, - backup_dir=backup_dir, - ) +function generate_interactive(pkg::AbstractString; fast::Bool=false) + t = interactive_template(; fast=fast) + generate(pkg, t) + return t end """ - gen_entrypoint( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} + gen_tests(pkg_dir::AbstractString, t::Template) -> Vector{String} -Create the module entrypoint in the temp package directory. +Create the test entrypoint in `pkg_dir`. # Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose entrypoint we are generating. +* `pkg_dir::AbstractString`: The package directory in which the files will be generated +* `t::Template`: The template whose tests we are generating. Returns an array of generated file/directory names. """ -function gen_entrypoint(dir::AbstractString, pkg_name::AbstractString, template::Template) - text = template.precompile ? "__precompile__()\n" : "" - text *= """ - module $pkg_name +function gen_tests(pkg_dir::AbstractString, t::Template) + proj = Base.current_project() + try + Pkg.activate(pkg_dir) + Pkg.add("Test") + 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 - # Package code goes here. - - end - """ - - gen_file(joinpath(dir, pkg_name, "src", "$pkg_name.jl"), text) - return ["src/"] -end - -""" - gen_tests( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} - -Create the test directory and entrypoint in the temp package directory. - -# Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose tests we are generating. - -Returns an array of generated file/directory names. -""" -function gen_tests(dir::AbstractString, pkg_name::AbstractString, template::Template) + pkg = basename(pkg_dir) text = """ - using $pkg_name - using Base.Test + using $pkg + using Test - @testset "$pkg_name.jl" begin + @testset "$pkg.jl" begin # Write your own tests here. @test 1 == 2 end """ - gen_file(joinpath(dir, pkg_name, "test", "runtests.jl"), text) - return ["test/"] + gen_file(joinpath(pkg_dir, "test", "runtests.jl"), text) + # TODO: Should we be checking Manifest.toml into Git? + return ["Manifest.toml", "test/"] end """ - gen_require( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} + gen_require(pkg_dir::AbstractString, t::Template) -> Vector{String} -Create the `REQUIRE` file in the temp package directory. +Create the `REQUIRE` file in `pkg_dir`. # Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose REQUIRE we are generating. +* `pkg_dir::AbstractString`: The directory in which the files will be generated. +* `t::Template`: The template whose REQUIRE we are generating. Returns an array of generated file/directory names. """ -function gen_require(dir::AbstractString, pkg_name::AbstractString, template::Template) - text = "julia $(version_floor(template.julia_version))\n" - text *= join(template.requirements, "\n") - - gen_file(joinpath(dir, pkg_name, "REQUIRE"), text) +function gen_require(pkg_dir::AbstractString, t::Template) + text = "julia $(version_floor(t.julia_version))\n" + gen_file(joinpath(pkg_dir, "REQUIRE"), text) return ["REQUIRE"] end """ - gen_readme( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} + gen_readme(pkg_dir::AbstractString, t::Template) -> Vector{String} -Create a README in the temp package directory with badges for each enabled plugin. +Create a README in `pkg_dir` with badges for each enabled plugin. # Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose README we are generating. +* `pkg_dir::AbstractString`: The directory in which the files will be generated. +* `t::Template`: The template whose README we are generating. Returns an array of generated file/directory names. """ -function gen_readme(dir::AbstractString, pkg_name::AbstractString, template::Template) - text = "# $pkg_name\n" +function gen_readme(pkg_dir::AbstractString, t::Template) + pkg = basename(pkg_dir) + text = "# $pkg\n" done = [] # Generate the ordered badges first, then add any remaining ones to the right. for plugin_type in BADGE_ORDER - if haskey(template.plugins, plugin_type) + if haskey(t.plugins, plugin_type) text *= "\n" text *= join( - badges(template.plugins[plugin_type], template.user, pkg_name), + badges(t.plugins[plugin_type], t.user, pkg), "\n", ) push!(done, plugin_type) end end - for plugin_type in setdiff(keys(template.plugins), done) + for plugin_type in setdiff(keys(t.plugins), done) text *= "\n" text *= join( - badges(template.plugins[plugin_type], template.user, pkg_name), + badges(t.plugins[plugin_type], t.user, pkg), "\n", ) end - gen_file(joinpath(dir, pkg_name, "README.md"), text) + gen_file(joinpath(pkg_dir, "README.md"), text) return ["README.md"] end """ - gen_gitignore( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} + gen_gitignore(pkg_dir::AbstractString, t::Template) -> Vector{String} -Create a `.gitignore` in the temp package directory. +Create a `.gitignore` in `pkg_dir`. # Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose .gitignore we are generating. +* `pkg_dir::AbstractString`: The directory in which the files will be generated. +* `t::Template`: The template whose .gitignore we are generating. Returns an array of generated file/directory names. """ -function gen_gitignore(dir::AbstractString, pkg_name::AbstractString, template::Template) +function gen_gitignore(pkg_dir::AbstractString, t::Template) + pkg = basename(pkg_dir) seen = [".DS_Store"] - patterns = vcat(map(p -> p.gitignore, values(template.plugins))...) + patterns = vcat(map(p -> p.gitignore, values(t.plugins))...) for pattern in patterns if !in(pattern, seen) push!(seen, pattern) @@ -315,41 +190,35 @@ function gen_gitignore(dir::AbstractString, pkg_name::AbstractString, template:: end text = join(seen, "\n") - gen_file(joinpath(dir, pkg_name, ".gitignore"), text) + gen_file(joinpath(pkg_dir, ".gitignore"), text) return [".gitignore"] end """ - gen_license( - dir::AbstractString, - pkg_name::AbstractString, - template::Template, - ) -> Vector{String} + gen_license(pkg_dir::AbstractString, t::Template) -> Vector{String} -Create a license in the temp package directory. +Create a license in `pkg_dir`. # Arguments -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. -* `pkg_name::AbstractString`: Name of the package. -* `template::Template`: The template whose LICENSE we are generating. +* `pkg_dir::AbstractString`: The directory in which the files will be generated. +* `t::Template`: The template whose LICENSE we are generating. Returns an array of generated file/directory names. """ -function gen_license(dir::AbstractString, pkg_name::AbstractString, template::Template) - if isempty(template.license) +function gen_license(pkg_dir::AbstractString, t::Template) + if isempty(t.license) return String[] end - text = "Copyright (c) $(template.years) $(template.authors)\n" - text *= read_license(template.license) + text = "Copyright (c) $(Dates.year(Dates.now())) $(t.authors)\n" + text *= read_license(t.license) - gen_file(joinpath(dir, pkg_name, "LICENSE"), text) + gen_file(joinpath(pkg_dir, "LICENSE"), text) return ["LICENSE"] end """ - gen_file(file_path::AbstractString, text::AbstractString) -> Int + gen_file(file::AbstractString, text::AbstractString) -> Int Create a new file containing some given text. Always ends the file with a newline. @@ -364,9 +233,7 @@ function gen_file(file::AbstractString, text::AbstractString) if !endswith(text , "\n") text *= "\n" end - open(file, "w") do fp - return write(fp, text) - end + return write(file, text) end """ @@ -381,10 +248,10 @@ Returns "major.minor" for the most recent release version relative to v. For pre with v.minor == v.patch == 0, returns "major.minor-". """ function version_floor(v::VersionNumber=VERSION) - if isempty(v.prerelease) || v.patch > 0 - return "$(v.major).$(v.minor)" + return if isempty(v.prerelease) || v.patch > 0 + "$(v.major).$(v.minor)" else - return "$(v.major).$(v.minor)-" + "$(v.major).$(v.minor)-" end end @@ -424,6 +291,10 @@ function substitute( ) # d["AFTER"] is true whenever something needs to occur in a CI "after_script". d["AFTER"] = d["DOCUMENTER"] || d["CODECOV"] || d["COVERALLS"] + # d["COVERAGE"] is true whenever a coverage plugin is enabled. + # TODO: This doesn't handle user-defined coverage plugins. + # Maybe we need an abstract CoveragePlugin <: GenericPlugin? + d["COVERAGE"] = d["CODECOV"] || d["COVERALLS"] return substitute(template, merge(d, view)) end diff --git a/src/plugin.jl b/src/plugin.jl index 6f73ec6..00fa732 100644 --- a/src/plugin.jl +++ b/src/plugin.jl @@ -56,7 +56,7 @@ Generic plugins are plugins that add any number of patterns to the generated pac end end -interactive(plugin_type::Type{MyPlugin}) = interactive(plugin_type; file="my-plugin.toml") +interactive(::Type{MyPlugin}) = interactive(MyPlugin; file="my-plugin.toml") ``` The above plugin ignores files ending with `.mgp`, copies `defaults/my-plugin.toml` by @@ -87,7 +87,8 @@ end """ Custom plugins are plugins whose behaviour does not follow the [`GenericPlugin`](@ref) pattern. They can implement [`gen_plugin`](@ref), [`badges`](@ref), and -[`interactive`](@ref) in any way they choose. +[`interactive`](@ref) in any way they choose, as long as they conform to the usual type +signature. # Attributes * `gitignore::Vector{AbstractString}`: Array of patterns to be added to the `.gitignore` of @@ -101,30 +102,20 @@ pattern. They can implement [`gen_plugin`](@ref), [`badges`](@ref), and MyPlugin() = new([], rand() > 0.8) - function gen_plugin( - plugin::MyPlugin, - template::Template, - dir::AbstractString, - pkg_name::AbstractString, - ) - if plugin.lucky - text = substitute( - "You got lucky with {{PKGNAME}}, {{USER}}!", - template, - ) - gen_file(joinpath(dir, pkg_name, ".myplugin.yml"), text) + function gen_plugin(p::MyPlugin, t::Template, pkg_name::AbstractString) + return if p.lucky + text = substitute("You got lucky with {{PKGNAME}}, {{USER}}!", t) + gen_file(joinpath(t.dir, pkg_name, ".myplugin.yml"), text) + [".myplugin.yml"] else println("Maybe next time.") + String[] end end - function badges( - plugin::MyPlugin, - user::AbstractString, - pkg_name::AbstractString, - ) - if plugin.lucky - return [ + function badges(p::MyPlugin, user::AbstractString, pkg_name::AbstractString) + return if p.lucky + [ format(Badge( "You got lucky!", "https://myplugin.com/badge.png", @@ -132,12 +123,12 @@ pattern. They can implement [`gen_plugin`](@ref), [`badges`](@ref), and )), ] else - return String[] + String[] end end end -interactive(plugin_type::Type{MyPlugin}) = MyPlugin() +interactive(:Type{MyPlugin}) = MyPlugin() ``` This plugin doesn't do much, but it demonstrates how [`gen_plugin`](@ref), [`badges`](@ref) @@ -148,9 +139,6 @@ and [`interactive`](@ref) can be implemented using [`substitute`](@ref), Often, the contents of the config file that your plugin generates depends on variables like the package name, the user's username, etc. Template files (which are stored in `defaults`) can use [here](https://github.com/jverzani/Mustache.jl)'s syntax to define replacements. - -**Note**: Due to a bug in `Mustache`, conditionals can insert undesired newlines -(more detail [here](https://github.com/jverzani/Mustache.jl/issues/47)). """ abstract type CustomPlugin <: Plugin end @@ -171,103 +159,80 @@ A `Badge` contains the data necessary to generate a Markdown badge. end """ - format(b::Badge) + format(b::Badge) -> String Return `badge`'s data formatted as a Markdown string. """ format(b::Badge) = "[![$(b.hover)]($(b.image))]($(b.link))" """ - gen_plugin( - plugin::Plugin, - template::Template, - dir::AbstractString, - pkg_name::AbstractString - ) -> Vector{String} + gen_plugin(p::Plugin, t::Template, pkg_name::AbstractString) -> Vector{String} Generate any files associated with a plugin. # Arguments -* `plugin::Plugin`: Plugin whose files are being generated. -* `template::Template`: Template configuration. -* `dir::AbstractString`: The directory in which the files will be generated. Note that - this will be joined to `pkg_name`. +* `p::Plugin`: Plugin whose files are being generated. +* `t::Template`: Template configuration. * `pkg_name::AbstractString`: Name of the package. Returns an array of generated file/directory names. """ -function gen_plugin( - plugin::Plugin, - template::Template, - dir::AbstractString, - pkg_name::AbstractString, -) - return String[] -end +gen_plugin(::Plugin, ::Template, ::AbstractString) = String[] -function gen_plugin( - plugin::GenericPlugin, - template::Template, - dir::AbstractString, - pkg_name::AbstractString, -) - if plugin.src === nothing +function gen_plugin(p::GenericPlugin, t::Template, pkg_name::AbstractString) + if p.src === nothing return String[] end text = substitute( - read(plugin.src, String), - template; - view=merge(Dict("PKGNAME" => pkg_name), plugin.view), + read(p.src, String), + t; + view=merge(Dict("PKGNAME" => pkg_name), p.view), ) - gen_file(joinpath(dir, pkg_name, plugin.dest), text) - return [plugin.dest] + gen_file(joinpath(t.dir, pkg_name, p.dest), text) + return [p.dest] end """ - badges(plugin::Plugin, user::AbstractString, pkg_name::AbstractString) -> Vector{String} + badges(p::Plugin, user::AbstractString, pkg_name::AbstractString) -> Vector{String} Generate Markdown badges for the plugin. # Arguments -* `plugin::Plugin`: Plugin whose badges we are generating. +* `p::Plugin`: Plugin whose badges we are generating. * `user::AbstractString`: Username of the package creator. * `pkg_name::AbstractString`: Name of the package. Returns an array of Markdown badges. """ -badges(plugin::Plugin, user::AbstractString, pkg_name::AbstractString) = String[] +badges(::Plugin, ::AbstractString, ::AbstractString) = String[] -function badges(plugin::GenericPlugin, user::AbstractString, pkg_name::AbstractString) +function badges(p::GenericPlugin, user::AbstractString, pkg_name::AbstractString) # Give higher priority to replacements defined in the plugin's view. - view = merge(Dict("USER" => user, "PKGNAME" => pkg_name), plugin.view) - return map(b -> substitute(format(b), view), plugin.badges) + view = merge(Dict("USER" => user, "PKGNAME" => pkg_name), p.view) + return map(b -> substitute(format(b), view), p.badges) end """ - interactive( - plugin_type::Type{<:Plugin}; - file::Union{AbstractString, Nothing}="", - ) -> Plugin + interactive(t::Type{<:Plugin}; file::Union{AbstractString, Nothing}="") -> Plugin -Interactively create a plugin of type `plugin_type`, where `file` is the plugin type's -default config template with a non-standard name (for `MyPlugin`, this is anything but +Interactively create a plugin of type `t`, where `file` is the plugin type's default +config template with a non-standard name (for `MyPlugin`, this is anything but "myplugin.yml"). """ -function interactive( - plugin_type::Type{<:GenericPlugin}; - file::Union{AbstractString, Nothing}="", -) - plugin_name = String(split(string(plugin_type), ".")[end]) +function interactive(t::Type{<:GenericPlugin}; file::Union{AbstractString, Nothing}="") + name = string(nameof(t)) # By default, we expect the default plugin file template for a plugin called # "MyPlugin" to be called "myplugin.yml". - fn = file != nothing && isempty(file) ? "$(lowercase(plugin_name)).yml" : file + fn = file != nothing && isempty(file) ? "$(lowercase(name)).yml" : file default_config_file = fn == nothing ? fn : joinpath(DEFAULTS_DIR, fn) - print("$plugin_name: Enter the config template filename (\"None\" for no file) ") + + print("$name: Enter the config template filename (\"None\" for no file) ") if default_config_file == nothing print("[None]: ") else print("[$(replace(default_config_file, homedir() => "~"))]: ") end + config_file = readline() config_file = if uppercase(config_file) == "NONE" nothing @@ -276,5 +241,6 @@ function interactive( else config_file end - return plugin_type(; config_file=config_file) + + return t(; config_file=config_file) end diff --git a/src/plugins/documenter.jl b/src/plugins/documenter.jl index 51b4787..a7eb250 100644 --- a/src/plugins/documenter.jl +++ b/src/plugins/documenter.jl @@ -4,35 +4,35 @@ generation via [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl). """ abstract type Documenter <: CustomPlugin end -function gen_plugin( - plugin::Documenter, - template::Template, - dir::AbstractString, - pkg_name::AbstractString, -) - path = joinpath(dir, pkg_name) +function gen_plugin(p::Documenter, t::Template, pkg_name::AbstractString) + path = joinpath(t.dir, pkg_name) docs_dir = joinpath(path, "docs", "src") mkpath(docs_dir) - if !isempty(plugin.assets) + + assets_string = if !isempty(p.assets) mkpath(joinpath(docs_dir, "assets")) - for file in plugin.assets + for file in p.assets cp(file, joinpath(docs_dir, "assets", basename(file))) end + # We want something that looks like the following: # [ # assets/file1, # assets/file2, # ] tab = repeat(" ", 4) - assets_string = "[\n" - for asset in plugin.assets - assets_string *= """$(tab^2)"assets/$(basename(asset))",\n""" + s = "[\n" + for asset in p.assets + s *= """$(tab^2)"assets/$(basename(asset))",\n""" end - assets_string *= "$tab]" + s *= "$tab]" + + s else - assets_string = "[]" + "[]" end - text = """ + + make = """ using Documenter, $pkg_name makedocs(; @@ -41,21 +41,25 @@ function gen_plugin( pages=[ "Home" => "index.md", ], - repo="https://$(template.host)/$(template.user)/$pkg_name.jl/blob/{commit}{path}#L{line}", + repo="https://$(t.host)/$(t.user)/$pkg_name.jl/blob/{commit}{path}#L{line}", sitename="$pkg_name.jl", - authors="$(template.authors)", + authors="$(t.authors)", assets=$assets_string, ) """ + docs = """ + # $pkg_name.jl - gen_file(joinpath(dirname(docs_dir), "make.jl"), text) - open(joinpath(docs_dir, "index.md"), "w") do fp - write(fp, "# $pkg_name") - end - readme_path = joinpath(dir, pkg_name, "README.md") - if isfile(readme_path) - cp(readme_path, joinpath(docs_dir, "index.md"), force=true) - end + ```@index + ``` + + ```@autodocs + Modules = [$pkg_name] + ``` + """ + + gen_file(joinpath(dirname(docs_dir), "make.jl"), make) + gen_file(joinpath(docs_dir, "index.md"), docs) end function Base.show(io::IO, p::Documenter) @@ -77,8 +81,8 @@ function Base.show(io::IO, p::Documenter) n > 0 && print(io, ": $(join(map(g -> "\"$g\"", p.gitignore), ", "))") end -function interactive(plugin_type::Type{<:Documenter}) - t = nameof(plugin_type) - print("$t: Enter any Documenter asset files (separated by spaces) []: ") - return plugin_type(; assets=String.(split(readline()))) +function interactive(t::Type{<:Documenter}) + name = string(nameof(t)) + print("$name: Enter any Documenter asset files (separated by spaces) []: ") + return t(; assets=string.(split(readline()))) end diff --git a/src/plugins/githubpages.jl b/src/plugins/githubpages.jl index f4cd481..176fa33 100644 --- a/src/plugins/githubpages.jl +++ b/src/plugins/githubpages.jl @@ -38,27 +38,20 @@ function badges(::GitHubPages, user::AbstractString, pkg_name::AbstractString) ] end -function gen_plugin( - plugin::GitHubPages, - template::Template, - dir::AbstractString, - pkg_name::AbstractString, -) - invoke( - gen_plugin, Tuple{Documenter, Template, AbstractString, AbstractString}, - plugin, template, dir, pkg_name, - ) - if haskey(template.plugins, TravisCI) - docs_src = joinpath(dir, pkg_name, "docs", "src") +function gen_plugin(p::GitHubPages, t::Template, pkg_name::AbstractString) + invoke(gen_plugin, Tuple{Documenter, Template, AbstractString}, p, t, pkg_name) + + if haskey(t.plugins, TravisCI) + docs_src = joinpath(t.dir, pkg_name, "docs", "src") open(joinpath(dirname(docs_src), "make.jl"), "a") do file write( file, """ deploydocs(; - repo="github.com/$(template.user)/$pkg_name.jl", + repo="$(t.host)/$(t.user)/$pkg_name.jl", target="build", - julia="0.6", + julia="1.0", deps=nothing, make=nothing, ) diff --git a/src/plugins/gitlabci.jl b/src/plugins/gitlabci.jl index c91f4f2..ca0331a 100644 --- a/src/plugins/gitlabci.jl +++ b/src/plugins/gitlabci.jl @@ -55,11 +55,11 @@ generated repositories, and appropriate badge(s) to the README. end end -function interactive(plugin_type::Type{GitLabCI}) +function interactive(::Type{GitLabCI}) name = "GitLabCI" kwargs = Dict{Symbol, Any}() - default_config_file = joinpath(DEFAULTS_DIR, "gitlab-ci.yml") + print("$name: Enter the config template filename (\"None\" for no file) ") print("[$default_config_file]: ") config_file = readline() diff --git a/src/template.jl b/src/template.jl index 90fed90..539b876 100644 --- a/src/template.jl +++ b/src/template.jl @@ -12,34 +12,24 @@ Records common information used to generate a package. If you don't wish to manu create a template, you can use [`interactive_template`](@ref) instead. # Keyword Arguments -* `user::AbstractString=""`: GitHub username. If left unset, it will try to take the - value of a supplied git config's "github.user" key, then the global git config's - value. If neither is set, an `ArgumentError` is thrown. - **This is case-sensitive for some plugins, so take care to enter it correctly.** +* `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.** * `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. - For example, [`AppVeyor`](@ref)'s badge image will point to a GitHub-specific URL, - regardless of the value of `host`. * `license::AbstractString="MIT"`: Name of the package license. If an empty string is given, no license is created. [`available_licenses`](@ref) can be used to list all available licenses, and [`show_license`](@ref) can be used to print out a particular license's text. * `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 try to take the value of a supplied git config's "user.name" key, then the global - git config's value, if it is left unset. -* `years::Union{Integer, AbstractString}=$(Dates.year(Dates.today()))`: Copyright years on - the license. Can be supplied by a number, or a string such as "2016 - 2017". + 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. -* `precompile::Bool=true`: Whether or not to enable precompilation in generated packages. * `julia_version::VersionNumber=VERSION`: Minimum allowed Julia version. -* `requirements::Vector{<:AbstractString}=String[]`: Package requirements. If there are - duplicate requirements with different versions, i.e. ["PkgTemplates", "PkgTemplates - 0.1"], an `ArgumentError` is thrown. Each entry in this array will be copied into the - `REQUIRE` file of packages generated with this template. -* `gitconfig::Dict=Dict()`: Git configuration options. +* `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. """ @auto_hash_equals struct Template @@ -47,12 +37,9 @@ create a template, you can use [`interactive_template`](@ref) instead. host::AbstractString license::AbstractString authors::AbstractString - years::AbstractString dir::AbstractString - precompile::Bool julia_version::VersionNumber - requirements::Vector{AbstractString} - gitconfig::Dict + ssh::Bool plugins::Dict{DataType, Plugin} function Template(; @@ -60,18 +47,24 @@ create a template, you can use [`interactive_template`](@ref) instead. host::AbstractString="https://github.com", license::AbstractString="MIT", authors::Union{AbstractString, Vector{<:AbstractString}}="", - years::Union{Integer, AbstractString}=Dates.year(Dates.today()), dir::AbstractString=dev_dir(), - precompile::Bool=true, julia_version::VersionNumber=VERSION, - requirements::Vector{<:AbstractString}=String[], - gitconfig::Dict=Dict(), + ssh::Bool=false, plugins::Vector{<:Plugin}=Plugin[], ) - # If no username was set, look for one in a supplied git config, - # and then in the global git config. + # 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 + + # If no username was set, look for one in the global git config. + # Note: This is one of a few GitHub specifics. if isempty(user) - user = get(gitconfig, "github.user", LibGit2.getconfig("github.user", "")) + user = LibGit2.getconfig("github.user", "") end if isempty(user) throw(ArgumentError("No GitHub username found, set one with user=username")) @@ -83,79 +76,42 @@ create a template, you can use [`interactive_template`](@ref) instead. throw(ArgumentError("License '$license' is not available")) end - # If no author was set, look for one in the supplied git config, - # and then in the global git config. + # If no author was set, look for one in the global git config. if isempty(authors) - authors = get(gitconfig, "user.name", LibGit2.getconfig("user.name", "")) + authors = LibGit2.getconfig("user.name", "") elseif isa(authors, Vector) authors = join(authors, ", ") end - years = string(years) - dir = abspath(expanduser(dir)) - requirements_dedup = collect(Set(requirements)) - diff = length(requirements) - length(requirements_dedup) - names = map(t -> first(t), split.(requirements_dedup)) - if length(names) > length(Set(names)) - throw(ArgumentError( - "requirements contains duplicate packages with conflicting versions" - )) - elseif diff > 0 - @warn "Removed $(diff) duplicate$(diff == 1 ? "" : "s") from requirements" - end - plugin_dict = Dict{DataType, Plugin}(typeof(p) => p for p in plugins) if (length(plugins) != length(plugin_dict)) @warn "Plugin list contained duplicates, only the last of each type was kept" end - new( - user, host, license, authors, years, dir, precompile, - julia_version, requirements_dedup, gitconfig, plugin_dict, - ) + new(user, host, license, authors, dir, julia_version, ssh, plugin_dict) end end function Base.show(io::IO, t::Template) - maybe_none(s::AbstractString) = isempty(string(s)) ? "None" : string(s) + maybe(s::AbstractString) = isempty(string(s)) ? "None" : string(s) spc = " " println(io, "Template:") - println(io, "$spc→ User: $(maybe_none(t.user))") - println(io, "$spc→ Host: $(maybe_none(t.host))") + println(io, "$spc→ User: $(maybe(t.user))") + println(io, "$spc→ Host: $(maybe(t.host))") print(io, "$spc→ License: ") if isempty(t.license) println(io, "None") else - println(io, "$(t.license) ($(t.authors) $(t.years))") + println(io, "$(t.license) ($(t.authors) $(Dates.year(Dates.now())))") end - println(io, "$spc→ Package directory: $(replace(maybe_none(t.dir), homedir() => "~"))") - println(io, "$spc→ Precompilation enabled: $(t.precompile ? "Yes" : "No")") + println(io, "$spc→ Package directory: $(replace(maybe(t.dir), homedir() => "~"))") println(io, "$spc→ Minimum Julia version: v$(version_floor(t.julia_version))") - - n = length(t.requirements) - s = n == 1 ? "" : "s" - print(io, "$spc→ $n package requirement$s") - - if isempty(t.requirements) - println(io) - else - println(io, ": $(join(t.requirements, ", "))") - end - - print(io, "$spc→ Git configuration options:") - if isempty(t.gitconfig) - println(io, " None") - else - println(io) - for k in sort(collect(keys(t.gitconfig)); by=string) - println(io, "$(spc^2)• $k = $(t.gitconfig[k])") - end - end + println(io, "$spc→ SSH remote: $(t.ssh ? "Yes" : "No")") print(io, "$spc→ Plugins:") if isempty(t.plugins) @@ -214,12 +170,11 @@ function interactive_template(; fast::Bool=false) menu = RadioMenu(String["None", split(String(take!(io)), "\n")...]) # If the user breaks out of the menu with Ctrl-c, the result is -1, the absolute # value of which correponds to no license. - licenses[abs(request(menu))].first + first(licenses[abs(request(menu))]) end - # We don't need to ask for authors or copyright years if there is no license, + # We don't need to ask for authors if there is no license, # because the license is the only place that they matter. - kwargs[:authors] = if fast || isempty(kwargs[:license]) LibGit2.getconfig("user.name", "") else @@ -230,15 +185,6 @@ function interactive_template(; fast::Bool=false) isempty(authors) ? default_authors : authors end - kwargs[:years] = if fast || isempty(kwargs[:license]) - Dates.year(Dates.today()) - else - default_years = Dates.year(Dates.today()) - print("Enter the copyright year(s) [$default_years]: ") - years = readline() - isempty(years) ? default_years : years - end - kwargs[:dir] = if fast dev_dir() else @@ -248,13 +194,6 @@ function interactive_template(; fast::Bool=false) isempty(dir) ? default_dir : dir end - kwargs[:precompile] = if fast - true - else - print("Enable precompilation? [yes]: ") - !in(uppercase(readline()), ["N", "NO", "F", "FALSE"]) - end - kwargs[:julia_version] = if fast VERSION else @@ -264,28 +203,11 @@ function interactive_template(; fast::Bool=false) isempty(julia_version) ? default_julia_version : VersionNumber(julia_version) end - kwargs[:requirements] = if fast - String[] + kwargs[:ssh] = if fast + false else - print("Enter any Julia package requirements, (separated by spaces) []: ") - String.(split(readline())) - end - - kwargs[:gitconfig] = if fast - Dict() - else - gitconfig = Dict() - print("Enter any Git key-value pairs (one per line, separated by spaces) [None]: ") - while true - line = readline() - isempty(line) && break - tokens = split(line, " ", limit=2) - if haskey(gitconfig, tokens[1]) - @warn "Duplicate key '$(tokens[1])': Replacing old value '$(tokens[2])'" - end - gitconfig[tokens[1]] = tokens[2] - end - gitconfig + print("Set remote to SSH? [no]: ") + in(uppercase(readline()), ["Y", "YES", "T", "TRUE"]) end println("Select plugins:") diff --git a/test/interactive/interactive.jl b/test/interactive/interactive.jl index df4743b..23c5598 100644 --- a/test/interactive/interactive.jl +++ b/test/interactive/interactive.jl @@ -7,17 +7,15 @@ # Therefore, we skip any interactive tests on OSX builds. @testset "Interactive template creation" begin - write(stdin.buffer, "$me\n\n\r\n\n\n\n\nd") + write(stdin.buffer, "$me\n\n\r\n\n\nd") t = interactive_template() @test t.user == me @test t.host == "github.com" @test isempty(t.license) @test t.authors == LibGit2.getconfig("user.name", "") - @test t.years == string(Dates.year(Dates.today())) @test t.dir == default_dir @test t.julia_version == VERSION - @test isempty(t.requirements) - @test isempty(t.gitconfig) + @test !t.ssh @test isempty(t.plugins) if isempty(LibGit2.getconfig("github.user", "")) @@ -25,44 +23,35 @@ @test_throws ArgumentError t = interactive_template() end - write(stdin.buffer, "$me\ngitlab.com\n$('\x1b')[B\r$me\n2016\n$test_file\nno\n0.5\nX Y\nkey val val\nkey2 val2\n\n$('\x1b')[B\r$('\x1b')[B\rd\n\n") + down = '\x1b' * "[B" # Down array key. + write(stdin.buffer, "$me\ngitlab.com\n$down\r$me\n$test_file\n0.5\nyes\n$down\r$down\rd\n\n") t = interactive_template() @test t.user == me @test t.host == "gitlab.com" # Not sure if the order the licenses are displayed in is consistent. @test !isempty(t.license) @test t.authors == me - @test t.years == "2016" @test t.dir == abspath(test_file) - @test !t.precompile @test t.julia_version == v"0.5.0" - @test Set(t.requirements) == Set(["X", "Y"]) - @test t.gitconfig == Dict("key" => "val val", "key2" => "val2") + @test t.ssh # Like above, not sure which plugins this will generate. @test length(t.plugins) == 2 - write(stdin.buffer, "$me\n\n\r\n\n\n\nA B\nA B\n\nd") - @test_logs (:warn, r".+") match_mode=:any interactive_template() - write(stdin.buffer, "$me\nd") t = interactive_template(; fast=true) @test t.user == me @test t.host == "github.com" @test t.license == "MIT" @test t.authors == LibGit2.getconfig("user.name", "") - # I guess this could technically break if it runs on New Year's Eve... - @test t.years == string(Dates.year(Dates.today())) @test t.dir == default_dir @test t.julia_version == VERSION - @test isempty(t.requirements) - @test isempty(t.gitconfig) + @test !t.ssh @test isempty(t.plugins) println() end @testset "Interactive package generation" begin - cfg = join(("$k $v" for (k, v) in gitconfig), "\n") - write(stdin.buffer, "$me\n\n\r\n\n\n\n$cfg\n\nd") + write(stdin.buffer, "$me\n\n\r\n\n\n\n\n\nd") generate_interactive(test_pkg) @test isdir(joinpath(default_dir, test_pkg)) rm(joinpath(default_dir, test_pkg); force=true, recursive=true) diff --git a/test/plugins/appveyor.jl b/test/plugins/appveyor.jl index 5aa304b..8b29208 100644 --- a/test/plugins/appveyor.jl +++ b/test/plugins/appveyor.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "AppVeyor" begin @testset "Plugin creation" begin @@ -26,47 +24,34 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = AppVeyor() - @test badges(p, user, test_pkg) == ["[![Build Status](https://ci.appveyor.com/api/projects/status/github/$user/$test_pkg.jl?svg=true)](https://ci.appveyor.com/project/$user/$test_pkg-jl)"] + @test badges(p, me, test_pkg) == ["[![Build Status](https://ci.appveyor.com/api/projects/status/github/$me/$test_pkg.jl?svg=true)](https://ci.appveyor.com/project/$me/$test_pkg-jl)"] end @testset "File generation" begin # Without a coverage plugin in the template, there should be no post-test step. p = AppVeyor() - @test gen_plugin(p, t, temp_dir, test_pkg) == [".appveyor.yml"] + @test gen_plugin(p, t, test_pkg) == [".appveyor.yml"] @test isfile(joinpath(pkg_dir, ".appveyor.yml")) appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String) - @test !occursin("coverage=true", appveyor) - @test !occursin("after_test", appveyor) - @test !occursin("Codecov.submit", appveyor) - @test !occursin("Coveralls.submit", appveyor) + @test !occursin("on_success", appveyor) + @test !occursin("%JL_CODECOV_SCRIPT%", appveyor) rm(joinpath(pkg_dir, ".appveyor.yml")) # Generating the plugin with CodeCov in the template should create a post-test step. t.plugins[CodeCov] = CodeCov() - gen_plugin(p, t, temp_dir, test_pkg) + gen_plugin(p, t, test_pkg) delete!(t.plugins, CodeCov) appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String) - @test occursin("coverage=true", appveyor) - @test occursin("after_test", appveyor) - @test occursin("Codecov.submit", appveyor) - @test !occursin("Coveralls.submit", appveyor) + @test occursin("on_success", appveyor) + @test occursin("%JL_CODECOV_SCRIPT%", appveyor) rm(joinpath(pkg_dir, ".appveyor.yml")) - # Coveralls should do the same. - t.plugins[Coveralls] = Coveralls() - gen_plugin(p, t, temp_dir, test_pkg) - delete!(t.plugins, Coveralls) - appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String) - @test occursin("coverage=true", appveyor) - @test occursin("after_test", appveyor) - @test occursin("Coveralls.submit", appveyor) - @test !occursin("Codecov.submit", appveyor) - rm(joinpath(pkg_dir, ".appveyor.yml")) + # TODO: Add Coveralls tests when AppVeyor.jl supports it. p = AppVeyor(; config_file=nothing) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) @test !isfile(joinpath(pkg_dir, ".appveyor.yml")) end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/plugins/codecov.jl b/test/plugins/codecov.jl index e410363..8fe836f 100644 --- a/test/plugins/codecov.jl +++ b/test/plugins/codecov.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "CodeCov" begin @testset "Plugin creation" begin @@ -26,17 +24,18 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = CodeCov() - @test badges(p, user, test_pkg) == ["[![CodeCov](https://codecov.io/gh/$user/$test_pkg.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/$user/$test_pkg.jl)"] + @test badges(p, me, test_pkg) == ["[![CodeCov](https://codecov.io/gh/$me/$test_pkg.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/$me/$test_pkg.jl)"] end @testset "File generation" begin p = CodeCov() - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) - @test !isfile(joinpath(pkg_dir, ".codecov.yml")) - p = CodeCov(; config_file=nothing) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) @test !isfile(joinpath(pkg_dir, ".codecov.yml")) + + p = CodeCov(; config_file=test_file) + @test gen_plugin(p, t, test_pkg) == [".codecov.yml"] + @test isfile(joinpath(pkg_dir, ".codecov.yml")) end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/plugins/coveralls.jl b/test/plugins/coveralls.jl index 3b257c2..f74e219 100644 --- a/test/plugins/coveralls.jl +++ b/test/plugins/coveralls.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "Coveralls" begin @testset "Plugin creation" begin @@ -26,18 +24,17 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = Coveralls() - @test badges(p, user, test_pkg) == ["[![Coveralls](https://coveralls.io/repos/github/$user/$test_pkg.jl/badge.svg?branch=master)](https://coveralls.io/github/$user/$test_pkg.jl?branch=master)"] + @test badges(p, me, test_pkg) == ["[![Coveralls](https://coveralls.io/repos/github/$me/$test_pkg.jl/badge.svg?branch=master)](https://coveralls.io/github/$me/$test_pkg.jl?branch=master)"] end @testset "File generation" begin p = Coveralls() - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) @test !isfile(joinpath(pkg_dir, ".coveralls.yml")) p = Coveralls(; config_file=test_file) - @test gen_plugin(p, t, temp_dir, test_pkg) == [".coveralls.yml"] + @test gen_plugin(p, t, test_pkg) == [".coveralls.yml"] @test isfile(joinpath(pkg_dir, ".coveralls.yml")) - rm(joinpath(pkg_dir, ".coveralls.yml")) end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/plugins/githubpages.jl b/test/plugins/githubpages.jl index 7c45151..9681b74 100644 --- a/test/plugins/githubpages.jl +++ b/test/plugins/githubpages.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "GitHubPages" begin @testset "Plugin creation" begin @@ -15,15 +13,15 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = GitHubPages() - @test badges(p, user, test_pkg) == [ - "[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://$user.github.io/$test_pkg.jl/stable)" - "[![Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://$user.github.io/$test_pkg.jl/latest)" + @test badges(p, me, test_pkg) == [ + "[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://$me.github.io/$test_pkg.jl/stable)" + "[![Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://$me.github.io/$test_pkg.jl/latest)" ] end @testset "File generation" begin p = GitHubPages() - @test gen_plugin(p, t, temp_dir, test_pkg) == ["docs/"] + @test gen_plugin(p, t, test_pkg) == ["docs/"] @test isdir(joinpath(pkg_dir, "docs")) @test isfile(joinpath(pkg_dir, "docs", "make.jl")) make = readchomp(joinpath(pkg_dir, "docs", "make.jl")) @@ -32,10 +30,10 @@ pkg_dir = joinpath(temp_dir, test_pkg) @test isdir(joinpath(pkg_dir, "docs", "src")) @test isfile(joinpath(pkg_dir, "docs", "src", "index.md")) index = readchomp(joinpath(pkg_dir, "docs", "src", "index.md")) - @test index == "# $test_pkg" + @test occursin("autodocs", index) rm(joinpath(pkg_dir, "docs"); recursive=true) p = GitHubPages(; assets=[test_file]) - @test gen_plugin(p, t, temp_dir, test_pkg) == ["docs/"] + @test gen_plugin(p, t, test_pkg) == ["docs/"] make = readchomp(joinpath(pkg_dir, "docs", "make.jl")) # Check the formatting of the assets list. @test occursin( @@ -49,11 +47,22 @@ pkg_dir = joinpath(temp_dir, test_pkg) @test isfile(joinpath(pkg_dir, "docs", "src", "assets", basename(test_file))) rm(joinpath(pkg_dir, "docs"); recursive=true) t.plugins[TravisCI] = TravisCI() - @test gen_plugin(p, t, temp_dir, test_pkg) == ["docs/"] + @test gen_plugin(p, t, test_pkg) == ["docs/"] make = readchomp(joinpath(pkg_dir, "docs", "make.jl")) @test occursin("deploydocs", make) rm(joinpath(pkg_dir, "docs"); recursive=true) end + + @testset "Package generation with GitHubPages plugin" begin + temp_dir = mktempdir() + t = Template(; user=me, dir=temp_dir, plugins=[GitHubPages()]) + generate(test_pkg, t) + + # Check that the gh-pages branch exists. + repo = LibGit2.GitRepo(joinpath(t.dir, test_pkg)) + branches = map(b -> LibGit2.shortname(first(b)), LibGit2.GitBranchIter(repo)) + @test in("gh-pages", branches) + end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/plugins/gitlabci.jl b/test/plugins/gitlabci.jl index c3523c2..e556156 100644 --- a/test/plugins/gitlabci.jl +++ b/test/plugins/gitlabci.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "GitLabCI" begin @testset "Plugin creation" begin @@ -40,33 +38,32 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = GitLabCI() - @test badges(p, user, test_pkg) == [ - "[![Build Status](https://gitlab.com/$user/$test_pkg.jl/badges/master/build.svg)](https://gitlab.com/$user/$test_pkg.jl/pipelines)", - "[![Coverage](https://gitlab.com/$user/$test_pkg.jl/badges/master/coverage.svg)](https://gitlab.com/$user/$test_pkg.jl/commits/master)", + @test badges(p, me, test_pkg) == [ + "[![Build Status](https://gitlab.com/$me/$test_pkg.jl/badges/master/build.svg)](https://gitlab.com/$me/$test_pkg.jl/pipelines)", + "[![Coverage](https://gitlab.com/$me/$test_pkg.jl/badges/master/coverage.svg)](https://gitlab.com/$me/$test_pkg.jl/commits/master)", ] end @testset "File generation" begin p = GitLabCI() - @test gen_plugin(p, t, temp_dir, test_pkg) == [".gitlab-ci.yml"] + @test gen_plugin(p, t, test_pkg) == [".gitlab-ci.yml"] @test isfile(joinpath(pkg_dir, ".gitlab-ci.yml")) gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String) - @test occursin("test_template", gitlab) # The default plugin should enable the coverage step. @test occursin("using Coverage", gitlab) rm(joinpath(pkg_dir, ".gitlab-ci.yml")) p = GitLabCI(; coverage=false) - gen_plugin(p, t, temp_dir, test_pkg) + gen_plugin(p, t, test_pkg) gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String) # If coverage is false, there should be no coverage step. @test !occursin("using Coverage", gitlab) rm(joinpath(pkg_dir, ".gitlab-ci.yml")) p = GitLabCI(; config_file=nothing) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) @test !isfile(joinpath(pkg_dir, ".gitlab-ci.yml")) end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/plugins/travisci.jl b/test/plugins/travisci.jl index db0056f..b5ef4b6 100644 --- a/test/plugins/travisci.jl +++ b/test/plugins/travisci.jl @@ -1,7 +1,5 @@ -user = gitconfig["github.user"] t = Template(; user=me) -temp_dir = mktempdir() -pkg_dir = joinpath(temp_dir, test_pkg) +pkg_dir = joinpath(t.dir, test_pkg) @testset "TravisCI" begin @testset "Plugin creation" begin @@ -26,13 +24,13 @@ pkg_dir = joinpath(temp_dir, test_pkg) @testset "Badge generation" begin p = TravisCI() - @test badges(p, user, test_pkg) == ["[![Build Status](https://travis-ci.com/$user/$test_pkg.jl.svg?branch=master)](https://travis-ci.com/$user/$test_pkg.jl)"] + @test badges(p, me, test_pkg) == ["[![Build Status](https://travis-ci.com/$me/$test_pkg.jl.svg?branch=master)](https://travis-ci.com/$me/$test_pkg.jl)"] end @testset "File generation" begin # Without a coverage plugin in the template, there should be no post-test step. p = TravisCI() - @test gen_plugin(p, t, temp_dir, test_pkg) == [".travis.yml"] + @test gen_plugin(p, t, test_pkg) == [".travis.yml"] @test isfile(joinpath(pkg_dir, ".travis.yml")) travis = read(joinpath(pkg_dir, ".travis.yml"), String) @@ -44,7 +42,7 @@ pkg_dir = joinpath(temp_dir, test_pkg) # Generating the plugin with CodeCov in the template should create a post-test step. t.plugins[CodeCov] = CodeCov() - gen_plugin(p, t, temp_dir, test_pkg) + gen_plugin(p, t, test_pkg) delete!(t.plugins, CodeCov) travis = read(joinpath(pkg_dir, ".travis.yml"), String) @test occursin("after_success", travis) @@ -55,7 +53,7 @@ pkg_dir = joinpath(temp_dir, test_pkg) # Coveralls should do the same. t.plugins[Coveralls] = Coveralls() - gen_plugin(p, t, temp_dir, test_pkg) + gen_plugin(p, t, test_pkg) delete!(t.plugins, Coveralls) travis = read(joinpath(pkg_dir, ".travis.yml"), String) @test occursin("after_success", travis) @@ -66,7 +64,7 @@ pkg_dir = joinpath(temp_dir, test_pkg) # With a Documenter plugin, there should be a docs deployment step. t.plugins[GitHubPages] = GitHubPages() - gen_plugin(p, t, temp_dir, test_pkg) + gen_plugin(p, t, test_pkg) delete!(t.plugins, GitHubPages) travis = read(joinpath(pkg_dir, ".travis.yml"), String) @test occursin("after_success", travis) @@ -76,9 +74,9 @@ pkg_dir = joinpath(temp_dir, test_pkg) rm(joinpath(pkg_dir, ".travis.yml")) p = TravisCI(; config_file=nothing) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) @test !isfile(joinpath(pkg_dir, ".travis.yml")) end end -rm(temp_dir; recursive=true) +rm(pkg_dir; recursive=true) diff --git a/test/runtests.jl b/test/runtests.jl index 0bc2150..e1abc32 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,9 +4,9 @@ using Dates using LibGit2 import PkgTemplates: badges, version_floor, substitute, read_license, gen_file, gen_readme, - gen_tests, gen_license, gen_require, gen_entrypoint, gen_gitignore, gen_plugin, - show_license, LICENSES, LICENSE_DIR, Plugin, GenericPlugin, CustomPlugin, Badge, - format, interactive, DEFAULTS_DIR + gen_tests, gen_license, gen_require, gen_gitignore, gen_plugin, show_license, LICENSES, + LICENSE_DIR, Plugin, GenericPlugin, CustomPlugin, Badge, format, interactive, + DEFAULTS_DIR mktempdir() do temp_dir mkdir(joinpath(temp_dir, "dev")) diff --git a/test/tests.jl b/test/tests.jl index 427ac1a..d8a1c88 100644 --- a/test/tests.jl +++ b/test/tests.jl @@ -16,13 +16,8 @@ struct Baz <: Plugin end # Various options to be passed into templates. const me = "christopher-dG" -const gitconfig = Dict( - "user.name" => "Tester McTestFace", - "user.email" => "email@web.site", - "github.user" => "TesterMcTestFace", -) const test_pkg = "TestPkg" -const fake_path = string(hash("/this/file/does/not/exist"); base=2) +const fake_path = "/dev/null/this/file/does/not/exist" const test_file = tempname() const default_dir = PkgTemplates.dev_dir() const template_text = """ @@ -41,11 +36,9 @@ write(test_file, template_text) t = Template(; user=me) @test t.user == me @test t.license == "MIT" - @test t.years == string(Dates.year(Dates.today())) @test t.authors == LibGit2.getconfig("user.name", "") @test t.dir == default_dir @test t.julia_version == VERSION - @test isempty(t.gitconfig) @test isempty(t.plugins) # Checking non-default field assignments. @@ -56,14 +49,8 @@ write(test_file, template_text) t = Template(; user=me, license="MPL") @test t.license == "MPL" - t = Template(; user=me, years=2014) - @test t.years == "2014" - t = Template(user=me, years="2014-2015") - @test t.years == "2014-2015" - t = Template(; user=me, authors="Some Guy") @test t.authors == "Some Guy" - # Vectors of authors should be comma-joined. t = Template(; user=me, authors=["Guy", "Gal"]) @test t.authors == "Guy, Gal" @@ -76,36 +63,9 @@ write(test_file, template_text) @test t.dir == joinpath(homedir(), basename(test_file)) end - t = Template(; user=me, precompile=false) - @test !t.precompile - t = Template(; user=me, julia_version=v"0.1.2") @test t.julia_version == v"0.1.2" - t = Template(; user=me, requirements=["$test_pkg 0.1"]) - @test t.requirements == ["$test_pkg 0.1"] - # Duplicate requirements should warn. - @test_logs (:warn, r".+") t = Template(; user=me, requirements=[test_pkg, test_pkg]) - @test t.requirements == [test_pkg] - # Duplicate requirements with non-matching versions should throw. - @test_throws ArgumentError Template(; - user=me, - requirements=[test_pkg, "$test_pkg 0.1"] - ) - - t = Template(; user=me, gitconfig=gitconfig) - @test t.gitconfig == gitconfig - - # Git options should be used as fallbacks for template user and authors. - # But an explicitly passed username trumps the gitconfig. - - t = Template(; user=me, gitconfig=gitconfig) - @test t.authors == gitconfig["user.name"] - - t = Template(; gitconfig=gitconfig) - @test t.user == gitconfig["github.user"] - @test t.authors == gitconfig["user.name"] - # The template should contain whatever plugins you give it. t = Template(; user=me, @@ -147,30 +107,24 @@ end @testset "Show methods" begin pkg_dir = replace(default_dir, homedir() => "~") buf = IOBuffer() - t = Template(; user=me, gitconfig=gitconfig) + t = Template(; user=me) show(buf, t) text = String(take!(buf)) expected = """ Template: → User: $me → Host: github.com - → License: MIT ($(gitconfig["user.name"]) $(Dates.year(now()))) + → License: MIT ($(LibGit2.getconfig("user.name", "")) $(Dates.year(now()))) → Package directory: $pkg_dir - → Precompilation enabled: Yes → Minimum Julia version: v$(PkgTemplates.version_floor()) - → 0 package requirements - → Git configuration options: - • github.user = $(gitconfig["github.user"]) - • user.email = $(gitconfig["user.email"]) - • user.name = $(gitconfig["user.name"]) + → SSH remote: No → Plugins: None """ @test text == rstrip(expected) t = Template( user=me, license="", - requirements=["Foo", "Bar"], - gitconfig=gitconfig, + ssh=true, plugins=[ TravisCI(), CodeCov(), @@ -185,13 +139,8 @@ end → Host: github.com → License: None → Package directory: $pkg_dir - → Precompilation enabled: Yes → Minimum Julia version: v$(PkgTemplates.version_floor()) - → 2 package requirements: Bar, Foo - → Git configuration options: - • github.user = $(gitconfig["github.user"]) - • user.email = $(gitconfig["user.email"]) - • user.name = $(gitconfig["user.name"]) + → SSH remote: Yes → Plugins: • CodeCov: → Config file: None @@ -210,8 +159,6 @@ end t = Template(; user=me, license="MPL", - requirements=[test_pkg], - gitconfig=gitconfig, plugins=[Coveralls(), TravisCI(), CodeCov(), GitHubPages(), AppVeyor()], ) temp_dir = mktempdir() @@ -224,7 +171,7 @@ end rm(temp_file) # Test the README generation. - @test gen_readme(temp_dir, test_pkg, t) == ["README.md"] + @test gen_readme(pkg_dir, t) == ["README.md"] @test isfile(joinpath(pkg_dir, "README.md")) readme = readchomp(joinpath(pkg_dir, "README.md")) rm(joinpath(pkg_dir, "README.md")) @@ -240,7 +187,7 @@ end something(findfirst("coveralls", readme)).start # Plugins with badges but not in BADGE_ORDER should appear at the far right side. t.plugins[Foo] = Foo() - gen_readme(temp_dir, test_pkg, t) + gen_readme(pkg_dir, t) readme = readchomp(joinpath(pkg_dir, "README.md")) rm(joinpath(pkg_dir, "README.md")) @test <( @@ -249,7 +196,7 @@ end ) # Test the gitignore generation. - @test gen_gitignore(temp_dir, test_pkg, t) == [".gitignore"] + @test gen_gitignore(pkg_dir, t) == [".gitignore"] @test isfile(joinpath(pkg_dir, ".gitignore")) gitignore = read(joinpath(pkg_dir, ".gitignore"), String) rm(joinpath(pkg_dir, ".gitignore")) @@ -261,50 +208,37 @@ end end # Test the license generation. - @test gen_license(temp_dir, test_pkg, t) == ["LICENSE"] + @test gen_license(pkg_dir, t) == ["LICENSE"] @test isfile(joinpath(pkg_dir, "LICENSE")) license = readchomp(joinpath(pkg_dir, "LICENSE")) rm(joinpath(pkg_dir, "LICENSE")) @test occursin(t.authors, license) - @test occursin(t.years, license) @test occursin(read_license(t.license), license) - # Test the source code entrypoint generation. - @test gen_entrypoint(temp_dir, test_pkg, t) == ["src/"] - @test isdir(joinpath(pkg_dir, "src")) - @test isfile(joinpath(pkg_dir, "src", "$test_pkg.jl")) - entrypoint = readchomp(joinpath(pkg_dir, "src", "$test_pkg.jl")) - rm(joinpath(pkg_dir, "src"); recursive=true) - @test occursin("__precompile__()", entrypoint) - @test occursin("module $test_pkg", entrypoint) - t2 = Template(; user=me, precompile=false) - gen_entrypoint(temp_dir, test_pkg, t2) - entrypoint = readchomp(joinpath(pkg_dir, "src", "$test_pkg.jl")) - @test !occursin("__precompile__()", entrypoint) - @test occursin("module $test_pkg", entrypoint) - rm(joinpath(pkg_dir, "src"); recursive=true) - # Test the REQUIRE generation. - @test gen_require(temp_dir, test_pkg, t) == ["REQUIRE"] + @test gen_require(pkg_dir, t) == ["REQUIRE"] @test isfile(joinpath(pkg_dir, "REQUIRE")) vf = version_floor(t.julia_version) - @test readchomp(joinpath(pkg_dir, "REQUIRE")) == "julia $vf\n$test_pkg" + @test readchomp(joinpath(pkg_dir, "REQUIRE")) == "julia $vf" rm(joinpath(pkg_dir, "REQUIRE")) # Test the test generation. - @test gen_tests(temp_dir, test_pkg, t) == ["test/"] + @test gen_tests(pkg_dir, t) == ["Manifest.toml", "test/"] @test isdir(joinpath(pkg_dir, "test")) @test isfile(joinpath(pkg_dir, "test", "runtests.jl")) - runtests = readchomp(joinpath(pkg_dir, "test", "runtests.jl")) + @test isfile(joinpath(pkg_dir, "Manifest.toml")) + runtests = read(joinpath(pkg_dir, "test", "runtests.jl"), String) rm(joinpath(pkg_dir, "test"); recursive=true) @test occursin("using $test_pkg", runtests) - @test occursin("using Base.Test", runtests) + @test occursin("using Test", runtests) + manifest = read(joinpath(pkg_dir, "Manifest.toml"), String) + @test occursin("[[Test]]", manifest) rm(temp_dir; recursive=true) end @testset "Package generation" begin - t = Template(; user=me, gitconfig=gitconfig) + t = Template(; user=me) generate(test_pkg, t) pkg_dir = joinpath(default_dir, test_pkg) @@ -315,31 +249,31 @@ end @test isfile(joinpath(pkg_dir, ".gitignore")) @test isdir(joinpath(pkg_dir, "src")) @test isfile(joinpath(pkg_dir, "src", "$test_pkg.jl")) + @test isfile(joinpath(pkg_dir, "Project.toml")) @test isdir(joinpath(pkg_dir, "test")) @test isfile(joinpath(pkg_dir, "test", "runtests.jl")) - # Check the gitconfig. - repo = LibGit2.GitRepo(pkg_dir) - remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") - branches = map(b -> LibGit2.shortname(first(b)), LibGit2.GitBranchIter(repo)) - @test LibGit2.getconfig(repo, "user.name", "") == gitconfig["user.name"] + @test isfile(joinpath(pkg_dir, "Manifest.toml")) # Check the configured remote and branches. # Note: This test will fail on your system if you've configured Git # to replace all HTTPS URLs with SSH. + repo = LibGit2.GitRepo(pkg_dir) + remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") + branches = map(b -> LibGit2.shortname(first(b)), LibGit2.GitBranchIter(repo)) @test LibGit2.url(remote) == "https://github.com/$me/$test_pkg.jl" - @test in("master", branches) - @test !in("gh-pages", branches) + @test branches == ["master"] @test !LibGit2.isdirty(repo) rm(pkg_dir; recursive=true) - # Check that the remote is an SSH URL. - generate(t, test_pkg; ssh=true) # Test the reversed-arguments method. + # 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. repo = LibGit2.GitRepo(pkg_dir) remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") @test LibGit2.url(remote) == "git@github.com:$me/$test_pkg.jl.git" rm(pkg_dir; recursive=true) # Check that the remote is set correctly for non-default hosts. - t = Template(; user=me, host="gitlab.com", gitconfig=gitconfig) + t = Template(; user=me, host="gitlab.com") generate(test_pkg, t) repo = LibGit2.GitRepo(pkg_dir) remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") @@ -348,62 +282,10 @@ end # Check that the package ends up in the right directory. temp_dir = mktempdir() - t = Template(; user=me, dir=temp_dir, gitconfig=gitconfig) + t = Template(; user=me, dir=temp_dir) generate(test_pkg, t) @test isdir(joinpath(temp_dir, test_pkg)) rm(temp_dir; recursive=true) - - # Check that all the plugin files are generated. - t = Template(; - user=me, - license="", - gitconfig=gitconfig, - plugins=[AppVeyor(), GitHubPages(), Coveralls(), CodeCov(), TravisCI()], - ) - generate(test_pkg, t) - @test isdir(pkg_dir) - @test !isfile(joinpath(pkg_dir, "LICENSE")) - @test isfile(joinpath(pkg_dir, ".travis.yml")) - @test isfile(joinpath(pkg_dir, ".appveyor.yml")) - @test isdir(joinpath(pkg_dir, "docs")) - @test isfile(joinpath(pkg_dir, "docs", "make.jl")) - @test isdir(joinpath(pkg_dir, "docs", "src")) - @test isfile(joinpath(pkg_dir, "docs", "src", "index.md")) - # Test that the gh-pages exists for GitHubPages. - repo = LibGit2.GitRepo(pkg_dir) - @test LibGit2.getconfig(repo, "user.name", "") == gitconfig["user.name"] - branches = map(b -> LibGit2.shortname(first(b)), LibGit2.GitBranchIter(repo)) - @test in("gh-pages", branches) - @test !LibGit2.isdirty(repo) - rm(pkg_dir; recursive=true) - - # Check that an existing directory is removed when force is set. - mkdir(pkg_dir) - @test_throws ArgumentError generate(test_pkg, t) - generate(test_pkg, t; force=true) - @test isfile(joinpath(pkg_dir, "README.md")) - rm(pkg_dir; recursive=true) - - # Check that the backup directory mechanism works. - temp_file, io = mktemp() - close(io) - temp_dir = mktempdir() - t = Template(; user=me, dir=temp_file) - @test_logs (:warn, r".+") match_mode=:any generate(test_pkg, t; backup_dir=temp_dir) - rm(temp_dir; recursive=true) - temp_dir = mktempdir() - t = Template(; user=me, dir=joinpath(temp_file, "dir")) - @test_logs (:warn, r".+") match_mode=:any generate(test_pkg, t; backup_dir=temp_dir) - rm(temp_dir; recursive=true) - rm(temp_file) - - # Check that the generated docs root is just the copied README. - t = Template(; user=me, gitconfig=gitconfig, plugins=[GitHubPages()]) - generate(test_pkg, t) - readme = read(joinpath(pkg_dir, "README.md"), String) - index = read(joinpath(pkg_dir, "docs", "src", "index.md"), String) - @test readme == index - rm(pkg_dir; recursive=true) end @testset "Version floor" begin @@ -485,10 +367,8 @@ end end @testset "Plugins" begin - user = gitconfig["github.user"] t = Template(; user=me) - temp_dir = mktempdir() - pkg_dir = joinpath(temp_dir, test_pkg) + pkg_dir = joinpath(t.dir, test_pkg) # Check badge constructor and formatting. badge = Badge("A", "B", "C") @@ -498,12 +378,12 @@ end @test format(badge) == "[![A](B)](C)" p = Bar() - @test isempty(badges(p, user, test_pkg)) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(badges(p, me, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) p = Baz() - @test isempty(badges(p, user, test_pkg)) - @test isempty(gen_plugin(p, t, temp_dir, test_pkg)) + @test isempty(badges(p, me, test_pkg)) + @test isempty(gen_plugin(p, t, test_pkg)) include(joinpath("plugins", "travisci.jl")) include(joinpath("plugins", "appveyor.jl")) @@ -511,8 +391,6 @@ end include(joinpath("plugins", "codecov.jl")) include(joinpath("plugins", "coveralls.jl")) include(joinpath("plugins", "githubpages.jl")) - - rm(temp_dir; recursive=true) end rm(test_file)