Merge pull request #20 from invenia/cdg/1.0pkg

Generate packages for 1.0
This commit is contained in:
Curtis Vogt 2018-10-15 08:56:13 -05:00 committed by GitHub
commit e47672bff1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 511 additions and 881 deletions

View File

@ -17,6 +17,7 @@ before_script:
- git config --global user.name "Travis" - git config --global user.name "Travis"
- git config --global user.email "travis@example.com" - git config --global user.email "travis@example.com"
after_success: after_success:
- julia -e 'using Pkg; Pkg.add("Coverage")' - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; CodeCov.submit(process_folder())'
- julia -e '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"))' - julia -e 'using Pkg; Pkg.add("Documenter"); include(joinpath("docs", "make.jl"))'

View File

@ -36,10 +36,10 @@ deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[Mustache]] [[Mustache]]
deps = ["Pkg", "Test"] deps = ["Pkg", "Tables", "Test"]
git-tree-sha1 = "fb4b57a278d18434eff78bdc2c06238f6ee3c9e7" git-tree-sha1 = "455807b7c098d8a31f26792f685d5be250e83292"
uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
version = "0.5.1" version = "0.5.4"
[[Pkg]] [[Pkg]]
deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
@ -57,6 +57,12 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
deps = ["Serialization"] deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[Requires]]
deps = ["Test"]
git-tree-sha1 = "f6fbf4ba64d295e146e49e021207993b6b48c7d1"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "0.5.2"
[[SHA]] [[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
@ -66,6 +72,12 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[Sockets]] [[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc" 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]] [[Test]]
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

View File

@ -10,6 +10,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
URIParser = "30578b45-9adc-5946-b283-645ec420af67" URIParser = "30578b45-9adc-5946-b283-645ec420af67"

132
README.md
View File

@ -11,50 +11,73 @@ repeatable, and customizable way.**
## Installation ## Installation
`PkgTemplates` is registered in ```julia
[`METADATA.jl`](https://github.com/JuliaLang/METADATA.jl), so run (v1.0) pkg> add PkgTemplates
`Pkg.add("PkgTemplates")` for the latest release, or ```
`Pkg.clone("PkgTemplates")` for the development version.
## Usage ## Usage
The simplest template only requires your GitHub username. The simplest template requires no arguments.
```julia ```julia
julia> using PkgTemplates 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) julia> generate("MyPkg", t)
INFO: Initialized git repo at /tmp/tmpvaHVki/MyPkg Generating project MyPkg:
INFO: Made empty initial commit /Users/degraafc/.julia/dev/MyPkg/Project.toml
INFO: Set remote origin to https://github.com/myusername/MyPkg.jl /Users/degraafc/.julia/dev/MyPkg/src/MyPkg.jl
INFO: Staged 6 files/directories: src/, test/, REQUIRE, README.md, .gitignore, LICENSE [ Info: Initialized git repo at /Users/degraafc/.julia/dev/MyPkg
INFO: Committed files generated by PkgTemplates [ Info: Set remote origin to https://github.com/myusername/MyPkg.jl
INFO: Moved temporary package directory into /home/degraafc/.julia/v0.6/ Updating registry at `~/.julia/registries/General`
INFO: Finished 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 .gitignore
LICENSE LICENSE
Manifest.toml
Project.toml
README.md README.md
REQUIRE REQUIRE
src/MyPkg.jl src/MyPkg.jl
test/runtests.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
julia> t = Template(; julia> t = Template(;
user="myusername", user="myusername",
license="ISC", license="ISC",
authors=["Chris de Graaf", "Invenia Technical Computing Corporation"], authors=["Chris de Graaf", "Invenia Technical Computing Corporation"],
years="2016-2017",
dir=joinpath(homedir(), "code"), dir=joinpath(homedir(), "code"),
julia_version=v"0.5.2", julia_version=v"0.7",
requirements=["PkgTemplates"],
gitconfig=Dict("diff.renames" => true),
plugins=[ plugins=[
TravisCI(), TravisCI(),
CodeCov(), CodeCov(),
@ -62,30 +85,69 @@ julia> t = Template(;
AppVeyor(), AppVeyor(),
GitHubPages(), 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) julia> generate(t, "MyPkg2")
INFO: Initialized git repo at /tmp/tmpe0dWY5/MyPkg Generating project MyPkg2:
INFO: Applied git configuration /Users/degraafc/code/MyPkg2/Project.toml
INFO: Made empty initial commit /Users/degraafc/code/MyPkg2/src/MyPkg2.jl
INFO: Set remote origin to git@github.com:myusername/MyPkg.jl.git [ Info: Initialized git repo at /Users/degraafc/code/MyPkg2
INFO: Created empty gh-pages branch [ Info: Set remote origin to https://github.com/myusername/MyPkg2.jl
INFO: Staged 9 files/directories: src/, test/, REQUIRE, README.md, .gitignore, LICENSE, docs/, .appveyor.yml, .travis.yml [ Info: Created empty gh-pages branch
INFO: Committed files generated by PkgTemplates Resolving package versions...
INFO: Moved temporary package directory into /home/degraafc/code/ Updating `~/code/MyPkg2/Project.toml`
INFO: Finished [8dfed614] + Test
WARNING: Remember to push all created branches to your remote: git push --all 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 .appveyor.yml
.gitignore .gitignore
.travis.yml .travis.yml
LICENSE LICENSE
Manifest.toml
Project.toml
README.md README.md
REQUIRE REQUIRE
docs/make.jl docs/make.jl
docs/src/index.md docs/src/index.md
src/MyPkg.jl src/MyPkg2.jl
test/runtests.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 If that looks like a lot of work, you can also create templates interactively
with `interactive_template`: 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 And if that's **still** too much work for you, you can call
`interactive_template` with `fast=true` to use default values for everything `interactive_template` with `fast=true` to use default values for everything

View File

@ -1,49 +1,33 @@
# Documentation: https://github.com/JuliaCI/Appveyor.jl
environment: environment:
matrix: matrix:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/{{VERSION}}/julia-{{VERSION}}-latest-win32.exe" - julia_version: {{VERSION}}
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/{{VERSION}}/julia-{{VERSION}}-latest-win64.exe" - julia_version: nightly
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" platform:
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - x86
- x64
matrix: matrix:
allow_failures: allow_failures:
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - julia_version: nightly
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
branches: branches:
only: only:
- master - master
- /release-.*/ - /release-.*/
notifications: notifications:
- provider: Email - provider: Email
on_build_success: false on_build_success: false
on_build_failure: false on_build_failure: false
on_build_status_changed: false on_build_status_changed: false
install: install:
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
# 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
build_script: build_script:
# Need to convert from shallow to complete for Pkg.clone to work - echo "%JL_BUILD_SCRIPT%"
- IF EXIST .git\shallow (git fetch --unshallow) - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
- C:\projects\julia\bin\julia -e "versioninfo(); Pkg.clone(pwd(), \"{{PKGNAME}}\"); Pkg.build(\"{{PKGNAME}}\")"
test_script: test_script:
- C:\projects\julia\bin\julia -e "Pkg.test(\"{{PKGNAME}}\"{{#AFTER}}; coverage=true{{/AFTER}})" - echo "%JL_TEST_SCRIPT%"
{{#AFTER}} - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"
after_test:
{{#CODECOV}} {{#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}} {{/CODECOV}}
{{#COVERALLS}}
- C:\projects\julia\bin\julia -e "cd(Pkg.dir(\"{{PKGNAME}}\")); Pkg.add(\"Coverage\"); using Coverage; Coveralls.submit(process_folder())"
{{/COVERALLS}}
{{/AFTER}}

View File

@ -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}}: Julia {{VERSION}}:
image: julia:{{VERSION}} image: julia:{{VERSION}}
<<: *test_template script: julia --project='@.' -e 'using Pkg; Pkg.build(); Pkg.test({{#GITLABCOVERAGE}}; coverage=true{{/GITLABCOVERAGE}})'
Julia nightly:
image: staticfloat/julia:nightly-x64
allow_failure: true
<<: *test_template
{{#GITLABCOVERAGE}} {{#GITLABCOVERAGE}}
"Coverage":
stage: coverage
coverage: /Test Coverage (\d+\.\d+%)/ coverage: /Test Coverage (\d+\.\d+%)/
image: julia:{{VERSION}} after_script:
tags: - julia -e 'using Printf; using Pkg; Pkg.add("Coverage"); using Coverage; c, t = get_summary(process_folder()); @printf "Test Coverage %.2f%%\n" 100c/t'
- 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)'
{{/GITLABCOVERAGE}} {{/GITLABCOVERAGE}}

View File

@ -12,18 +12,15 @@ matrix:
fast_finish: true fast_finish: true
notifications: notifications:
email: false 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}}
after_success: after_success:
{{#CODECOV}} {{#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}} {{/CODECOV}}
{{#COVERALLS}} {{#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}} {{/COVERALLS}}
{{#DOCUMENTER}} {{#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}} {{/DOCUMENTER}}
{{/AFTER}} {{/AFTER}}

View File

@ -20,7 +20,7 @@ makedocs(;
deploydocs(; deploydocs(;
repo="github.com/invenia/PkgTemplates.jl", repo="github.com/invenia/PkgTemplates.jl",
target="build", target="build",
julia="0.6", julia="1.0",
deps=nothing, deps=nothing,
make=nothing, make=nothing,
) )

View File

@ -11,24 +11,23 @@ repeatable, and customizable way.**
## Installation ## Installation
`PkgTemplates` is registered in ```julia
[`METADATA.jl`](https://github.com/JuliaLang/METADATA.jl), so run (v1.0) pkg> add PkgTemplates
`Pkg.add("PkgTemplates")` for the latest release, or ```
`Pkg.clone("PkgTemplates")` for the development version.
## Usage ## Usage
The simplest template only requires your GitHub username. The simplest template requires no arguments.
```@repl ```@repl
using PkgTemplates using PkgTemplates
t = Template(; user="myusername"); t = Template()
generate("MyPkg", t) 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 However, we can also configure a number of keyword arguments to
[`Template`](@ref) and [`generate`](@ref): [`Template`](@ref):
```@repl ```@repl
using PkgTemplates using PkgTemplates
@ -36,11 +35,9 @@ t = Template(;
user="myusername", user="myusername",
license="MIT", license="MIT",
authors=["Chris de Graaf", "Invenia Technical Computing Corporation"], authors=["Chris de Graaf", "Invenia Technical Computing Corporation"],
years="2016-2017",
dir=joinpath(homedir(), "code"), dir=joinpath(homedir(), "code"),
julia_version=v"0.5.2", julia_version=v"0.7",
requirements=["PkgTemplates"], ssh=true,
gitconfig=Dict("diff.renames" => true),
plugins=[ plugins=[
TravisCI(), TravisCI(),
CodeCov(), CodeCov(),
@ -48,15 +45,15 @@ t = Template(;
AppVeyor(), AppVeyor(),
GitHubPages(), GitHubPages(),
], ],
); )
generate("MyPkg", t; force=true, ssh=true) generate("MyPkg2", t)
run(`git -C $(joinpath(t.dir, "MyPkg")) ls-tree -r --name-only HEAD`) 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 If that looks like a lot of work, you can also create templates interactively
with [`interactive_template`](@ref): 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 And if that's **still** too much work for you, you can call
`interactive_template` with `fast=true` to use default values for everything `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 You can also use [`generate_interactive`](@ref) to interactively generate a template and then
immediately use it to create a new package. 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. `PkgTemplates` is similar in functionality to
However, `PkgTemplates` offers more customizability in templates and more [`PkgDev`](https://github.com/JuliaLang/PkgDev.jl)'s `generate` function. However,
extensibility via plugins. For the package registration and release management `PkgTemplates` offers more customizability in templates and more extensibility via plugins.
features that `PkgTemplates` lacks, you are encouraged to use For the package registration and release management features that `PkgTemplates` doesn't
[AttoBot](https://github.com/apps/attobot) instead. 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).

View File

@ -24,7 +24,6 @@ generate_interactive
### Helper Functions ### Helper Functions
```@docs ```@docs
gen_entrypoint
gen_tests gen_tests
gen_require gen_require
gen_readme gen_readme

View File

@ -1,4 +1,3 @@
__precompile__()
module PkgTemplates module PkgTemplates
using AutoHashEquals using AutoHashEquals
@ -7,6 +6,7 @@ using Distributed
using InteractiveUtils using InteractiveUtils
using LibGit2 using LibGit2
using Mustache using Mustache
using Pkg
using REPL.TerminalMenus using REPL.TerminalMenus
using URIParser using URIParser

View File

@ -1,313 +1,188 @@
""" """
generate( generate(pkg_name::AbstractString, t::Template) -> Nothing
pkg_name::AbstractString,
t::Template;
force::Bool=false,
ssh::Bool=false,
backup_dir::AbstractString="",
) -> Nothing
Generate a package named `pkg_name` from `template`. Generate a package named `pkg_name` from `t`.
# 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`).
""" """
function generate( function generate(pkg_name::AbstractString, t::Template)
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)
pkg_dir = joinpath(t.dir, pkg_name) 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 try
mkpath(dirname(pkg_dir)) pkg_name = splitjl(pkg_name)
mv(temp_pkg_dir, pkg_dir; force=force) pkg_dir = joinpath(t.dir, pkg_name)
@info "Moved temporary package directory into $(t.dir)/"
catch # Likely cause is that t.dir can't be created (is a file, etc.). # Create the directory with some boilerplate inside.
# We're just going to trust that backup_dir is a valid directory. Pkg.generate(pkg_dir)
backup_dir = if isempty(backup_dir)
mktempdir() # 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 else
abspath(backup_dir) "https://$(t.host)/$(t.user)/$pkg_name.jl"
end end
mkpath(backup_dir) # We need to set the remote in a strange way, see #8.
mv(temp_pkg_dir, joinpath(backup_dir, pkg_name)) close(LibGit2.GitRemote(repo, "origin", rmt))
@warn "$pkg_name couldn't be moved into $pkg_dir, left package in $backup_dir" @info "Set remote origin to $rmt"
end
@info "Finished" # Create the gh-pages branch if necessary.
if multiple_branches if haskey(t.plugins, GitHubPages)
@info "Remember to push all created branches to your remote: git push --all" 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
end end
function generate( generate(t::Template, pkg_name::AbstractString) = generate(pkg_name, t)
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_interactive( generate_interactive(pkg::AbstractString; fast::Bool=false) -> Template
pkg_name::AbstractString;
force::Bool=false,
ssh::Bool=false,
backup_dir::AbstractString="",
fast::Bool=false,
) -> Nothing
Interactively create a template, and then generate a package with it. Arguments and 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 keywords are used in the same way as in [`generate`](@ref) and
[`interactive_template`](@ref). [`interactive_template`](@ref).
""" """
function generate_interactive( function generate_interactive(pkg::AbstractString; fast::Bool=false)
pkg_name::AbstractString; t = interactive_template(; fast=fast)
force::Bool=false, generate(pkg, t)
ssh::Bool=false, return t
backup_dir::AbstractString="",
fast::Bool=false,
)
generate(
pkg_name,
interactive_template(; fast=fast);
force=force,
ssh=ssh,
backup_dir=backup_dir,
)
end end
""" """
gen_entrypoint( gen_tests(pkg_dir::AbstractString, t::Template) -> Vector{String}
dir::AbstractString,
pkg_name::AbstractString,
template::Template,
) -> Vector{String}
Create the module entrypoint in the temp package directory. Create the test entrypoint in `pkg_dir`.
# Arguments # Arguments
* `dir::AbstractString`: The directory in which the files will be generated. Note that * `pkg_dir::AbstractString`: The package directory in which the files will be generated
this will be joined to `pkg_name`. * `t::Template`: The template whose tests we are generating.
* `pkg_name::AbstractString`: Name of the package.
* `template::Template`: The template whose entrypoint we are generating.
Returns an array of generated file/directory names. Returns an array of generated file/directory names.
""" """
function gen_entrypoint(dir::AbstractString, pkg_name::AbstractString, template::Template) function gen_tests(pkg_dir::AbstractString, t::Template)
text = template.precompile ? "__precompile__()\n" : "" proj = Base.current_project()
text *= """ try
module $pkg_name 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. pkg = basename(pkg_dir)
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)
text = """ text = """
using $pkg_name using $pkg
using Base.Test using Test
@testset "$pkg_name.jl" begin @testset "$pkg.jl" begin
# Write your own tests here. # Write your own tests here.
@test 1 == 2 @test 1 == 2
end end
""" """
gen_file(joinpath(dir, pkg_name, "test", "runtests.jl"), text) gen_file(joinpath(pkg_dir, "test", "runtests.jl"), text)
return ["test/"] # TODO: Should we be checking Manifest.toml into Git?
return ["Manifest.toml", "test/"]
end end
""" """
gen_require( gen_require(pkg_dir::AbstractString, t::Template) -> Vector{String}
dir::AbstractString,
pkg_name::AbstractString,
template::Template,
) -> Vector{String}
Create the `REQUIRE` file in the temp package directory. Create the `REQUIRE` file in `pkg_dir`.
# Arguments # Arguments
* `dir::AbstractString`: The directory in which the files will be generated. Note that * `pkg_dir::AbstractString`: The directory in which the files will be generated.
this will be joined to `pkg_name`. * `t::Template`: The template whose REQUIRE we are generating.
* `pkg_name::AbstractString`: Name of the package.
* `template::Template`: The template whose REQUIRE we are generating.
Returns an array of generated file/directory names. Returns an array of generated file/directory names.
""" """
function gen_require(dir::AbstractString, pkg_name::AbstractString, template::Template) function gen_require(pkg_dir::AbstractString, t::Template)
text = "julia $(version_floor(template.julia_version))\n" text = "julia $(version_floor(t.julia_version))\n"
text *= join(template.requirements, "\n") gen_file(joinpath(pkg_dir, "REQUIRE"), text)
gen_file(joinpath(dir, pkg_name, "REQUIRE"), text)
return ["REQUIRE"] return ["REQUIRE"]
end end
""" """
gen_readme( gen_readme(pkg_dir::AbstractString, t::Template) -> Vector{String}
dir::AbstractString,
pkg_name::AbstractString,
template::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 # Arguments
* `dir::AbstractString`: The directory in which the files will be generated. Note that * `pkg_dir::AbstractString`: The directory in which the files will be generated.
this will be joined to `pkg_name`. * `t::Template`: The template whose README we are generating.
* `pkg_name::AbstractString`: Name of the package.
* `template::Template`: The template whose README we are generating.
Returns an array of generated file/directory names. Returns an array of generated file/directory names.
""" """
function gen_readme(dir::AbstractString, pkg_name::AbstractString, template::Template) function gen_readme(pkg_dir::AbstractString, t::Template)
text = "# $pkg_name\n" pkg = basename(pkg_dir)
text = "# $pkg\n"
done = [] done = []
# Generate the ordered badges first, then add any remaining ones to the right. # Generate the ordered badges first, then add any remaining ones to the right.
for plugin_type in BADGE_ORDER for plugin_type in BADGE_ORDER
if haskey(template.plugins, plugin_type) if haskey(t.plugins, plugin_type)
text *= "\n" text *= "\n"
text *= join( text *= join(
badges(template.plugins[plugin_type], template.user, pkg_name), badges(t.plugins[plugin_type], t.user, pkg),
"\n", "\n",
) )
push!(done, plugin_type) push!(done, plugin_type)
end end
end end
for plugin_type in setdiff(keys(template.plugins), done) for plugin_type in setdiff(keys(t.plugins), done)
text *= "\n" text *= "\n"
text *= join( text *= join(
badges(template.plugins[plugin_type], template.user, pkg_name), badges(t.plugins[plugin_type], t.user, pkg),
"\n", "\n",
) )
end end
gen_file(joinpath(dir, pkg_name, "README.md"), text) gen_file(joinpath(pkg_dir, "README.md"), text)
return ["README.md"] return ["README.md"]
end end
""" """
gen_gitignore( gen_gitignore(pkg_dir::AbstractString, t::Template) -> Vector{String}
dir::AbstractString,
pkg_name::AbstractString,
template::Template,
) -> Vector{String}
Create a `.gitignore` in the temp package directory. Create a `.gitignore` in `pkg_dir`.
# Arguments # Arguments
* `dir::AbstractString`: The directory in which the files will be generated. Note that * `pkg_dir::AbstractString`: The directory in which the files will be generated.
this will be joined to `pkg_name`. * `t::Template`: The template whose .gitignore we are generating.
* `pkg_name::AbstractString`: Name of the package.
* `template::Template`: The template whose .gitignore we are generating.
Returns an array of generated file/directory names. 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"] 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 for pattern in patterns
if !in(pattern, seen) if !in(pattern, seen)
push!(seen, pattern) push!(seen, pattern)
@ -315,41 +190,35 @@ function gen_gitignore(dir::AbstractString, pkg_name::AbstractString, template::
end end
text = join(seen, "\n") text = join(seen, "\n")
gen_file(joinpath(dir, pkg_name, ".gitignore"), text) gen_file(joinpath(pkg_dir, ".gitignore"), text)
return [".gitignore"] return [".gitignore"]
end end
""" """
gen_license( gen_license(pkg_dir::AbstractString, t::Template) -> Vector{String}
dir::AbstractString,
pkg_name::AbstractString,
template::Template,
) -> Vector{String}
Create a license in the temp package directory. Create a license in `pkg_dir`.
# Arguments # Arguments
* `dir::AbstractString`: The directory in which the files will be generated. Note that * `pkg_dir::AbstractString`: The directory in which the files will be generated.
this will be joined to `pkg_name`. * `t::Template`: The template whose LICENSE we are generating.
* `pkg_name::AbstractString`: Name of the package.
* `template::Template`: The template whose LICENSE we are generating.
Returns an array of generated file/directory names. Returns an array of generated file/directory names.
""" """
function gen_license(dir::AbstractString, pkg_name::AbstractString, template::Template) function gen_license(pkg_dir::AbstractString, t::Template)
if isempty(template.license) if isempty(t.license)
return String[] return String[]
end end
text = "Copyright (c) $(template.years) $(template.authors)\n" text = "Copyright (c) $(Dates.year(Dates.now())) $(t.authors)\n"
text *= read_license(template.license) text *= read_license(t.license)
gen_file(joinpath(dir, pkg_name, "LICENSE"), text) gen_file(joinpath(pkg_dir, "LICENSE"), text)
return ["LICENSE"] return ["LICENSE"]
end 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. 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") if !endswith(text , "\n")
text *= "\n" text *= "\n"
end end
open(file, "w") do fp return write(file, text)
return write(fp, text)
end
end 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-". with v.minor == v.patch == 0, returns "major.minor-".
""" """
function version_floor(v::VersionNumber=VERSION) function version_floor(v::VersionNumber=VERSION)
if isempty(v.prerelease) || v.patch > 0 return if isempty(v.prerelease) || v.patch > 0
return "$(v.major).$(v.minor)" "$(v.major).$(v.minor)"
else else
return "$(v.major).$(v.minor)-" "$(v.major).$(v.minor)-"
end end
end end
@ -424,6 +291,10 @@ function substitute(
) )
# d["AFTER"] is true whenever something needs to occur in a CI "after_script". # d["AFTER"] is true whenever something needs to occur in a CI "after_script".
d["AFTER"] = d["DOCUMENTER"] || d["CODECOV"] || d["COVERALLS"] 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)) return substitute(template, merge(d, view))
end end

View File

@ -56,7 +56,7 @@ Generic plugins are plugins that add any number of patterns to the generated pac
end end
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 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) Custom plugins are plugins whose behaviour does not follow the [`GenericPlugin`](@ref)
pattern. They can implement [`gen_plugin`](@ref), [`badges`](@ref), and 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 # Attributes
* `gitignore::Vector{AbstractString}`: Array of patterns to be added to the `.gitignore` of * `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) MyPlugin() = new([], rand() > 0.8)
function gen_plugin( function gen_plugin(p::MyPlugin, t::Template, pkg_name::AbstractString)
plugin::MyPlugin, return if p.lucky
template::Template, text = substitute("You got lucky with {{PKGNAME}}, {{USER}}!", t)
dir::AbstractString, gen_file(joinpath(t.dir, pkg_name, ".myplugin.yml"), text)
pkg_name::AbstractString, [".myplugin.yml"]
)
if plugin.lucky
text = substitute(
"You got lucky with {{PKGNAME}}, {{USER}}!",
template,
)
gen_file(joinpath(dir, pkg_name, ".myplugin.yml"), text)
else else
println("Maybe next time.") println("Maybe next time.")
String[]
end end
end end
function badges( function badges(p::MyPlugin, user::AbstractString, pkg_name::AbstractString)
plugin::MyPlugin, return if p.lucky
user::AbstractString, [
pkg_name::AbstractString,
)
if plugin.lucky
return [
format(Badge( format(Badge(
"You got lucky!", "You got lucky!",
"https://myplugin.com/badge.png", "https://myplugin.com/badge.png",
@ -132,12 +123,12 @@ pattern. They can implement [`gen_plugin`](@ref), [`badges`](@ref), and
)), )),
] ]
else else
return String[] String[]
end end
end 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) 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 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`) 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. 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 abstract type CustomPlugin <: Plugin end
@ -171,103 +159,80 @@ A `Badge` contains the data necessary to generate a Markdown badge.
end end
""" """
format(b::Badge) format(b::Badge) -> String
Return `badge`'s data formatted as a Markdown string. Return `badge`'s data formatted as a Markdown string.
""" """
format(b::Badge) = "[![$(b.hover)]($(b.image))]($(b.link))" format(b::Badge) = "[![$(b.hover)]($(b.image))]($(b.link))"
""" """
gen_plugin( gen_plugin(p::Plugin, t::Template, pkg_name::AbstractString) -> Vector{String}
plugin::Plugin,
template::Template,
dir::AbstractString,
pkg_name::AbstractString
) -> Vector{String}
Generate any files associated with a plugin. Generate any files associated with a plugin.
# Arguments # Arguments
* `plugin::Plugin`: Plugin whose files are being generated. * `p::Plugin`: Plugin whose files are being generated.
* `template::Template`: Template configuration. * `t::Template`: Template configuration.
* `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. * `pkg_name::AbstractString`: Name of the package.
Returns an array of generated file/directory names. Returns an array of generated file/directory names.
""" """
function gen_plugin( gen_plugin(::Plugin, ::Template, ::AbstractString) = String[]
plugin::Plugin,
template::Template,
dir::AbstractString,
pkg_name::AbstractString,
)
return String[]
end
function gen_plugin( function gen_plugin(p::GenericPlugin, t::Template, pkg_name::AbstractString)
plugin::GenericPlugin, if p.src === nothing
template::Template,
dir::AbstractString,
pkg_name::AbstractString,
)
if plugin.src === nothing
return String[] return String[]
end end
text = substitute( text = substitute(
read(plugin.src, String), read(p.src, String),
template; t;
view=merge(Dict("PKGNAME" => pkg_name), plugin.view), view=merge(Dict("PKGNAME" => pkg_name), p.view),
) )
gen_file(joinpath(dir, pkg_name, plugin.dest), text) gen_file(joinpath(t.dir, pkg_name, p.dest), text)
return [plugin.dest] return [p.dest]
end 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. Generate Markdown badges for the plugin.
# Arguments # Arguments
* `plugin::Plugin`: Plugin whose badges we are generating. * `p::Plugin`: Plugin whose badges we are generating.
* `user::AbstractString`: Username of the package creator. * `user::AbstractString`: Username of the package creator.
* `pkg_name::AbstractString`: Name of the package. * `pkg_name::AbstractString`: Name of the package.
Returns an array of Markdown badges. 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. # Give higher priority to replacements defined in the plugin's view.
view = merge(Dict("USER" => user, "PKGNAME" => pkg_name), plugin.view) view = merge(Dict("USER" => user, "PKGNAME" => pkg_name), p.view)
return map(b -> substitute(format(b), view), plugin.badges) return map(b -> substitute(format(b), view), p.badges)
end end
""" """
interactive( interactive(t::Type{<:Plugin}; file::Union{AbstractString, Nothing}="") -> Plugin
plugin_type::Type{<:Plugin};
file::Union{AbstractString, Nothing}="",
) -> Plugin
Interactively create a plugin of type `plugin_type`, where `file` is the plugin type's Interactively create a plugin of type `t`, where `file` is the plugin type's default
default config template with a non-standard name (for `MyPlugin`, this is anything but config template with a non-standard name (for `MyPlugin`, this is anything but
"myplugin.yml"). "myplugin.yml").
""" """
function interactive( function interactive(t::Type{<:GenericPlugin}; file::Union{AbstractString, Nothing}="")
plugin_type::Type{<:GenericPlugin}; name = string(nameof(t))
file::Union{AbstractString, Nothing}="",
)
plugin_name = String(split(string(plugin_type), ".")[end])
# By default, we expect the default plugin file template for a plugin called # By default, we expect the default plugin file template for a plugin called
# "MyPlugin" to be called "myplugin.yml". # "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) 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 if default_config_file == nothing
print("[None]: ") print("[None]: ")
else else
print("[$(replace(default_config_file, homedir() => "~"))]: ") print("[$(replace(default_config_file, homedir() => "~"))]: ")
end end
config_file = readline() config_file = readline()
config_file = if uppercase(config_file) == "NONE" config_file = if uppercase(config_file) == "NONE"
nothing nothing
@ -276,5 +241,6 @@ function interactive(
else else
config_file config_file
end end
return plugin_type(; config_file=config_file)
return t(; config_file=config_file)
end end

View File

@ -4,35 +4,35 @@ generation via [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl).
""" """
abstract type Documenter <: CustomPlugin end abstract type Documenter <: CustomPlugin end
function gen_plugin( function gen_plugin(p::Documenter, t::Template, pkg_name::AbstractString)
plugin::Documenter, path = joinpath(t.dir, pkg_name)
template::Template,
dir::AbstractString,
pkg_name::AbstractString,
)
path = joinpath(dir, pkg_name)
docs_dir = joinpath(path, "docs", "src") docs_dir = joinpath(path, "docs", "src")
mkpath(docs_dir) mkpath(docs_dir)
if !isempty(plugin.assets)
assets_string = if !isempty(p.assets)
mkpath(joinpath(docs_dir, "assets")) mkpath(joinpath(docs_dir, "assets"))
for file in plugin.assets for file in p.assets
cp(file, joinpath(docs_dir, "assets", basename(file))) cp(file, joinpath(docs_dir, "assets", basename(file)))
end end
# We want something that looks like the following: # We want something that looks like the following:
# [ # [
# assets/file1, # assets/file1,
# assets/file2, # assets/file2,
# ] # ]
tab = repeat(" ", 4) tab = repeat(" ", 4)
assets_string = "[\n" s = "[\n"
for asset in plugin.assets for asset in p.assets
assets_string *= """$(tab^2)"assets/$(basename(asset))",\n""" s *= """$(tab^2)"assets/$(basename(asset))",\n"""
end end
assets_string *= "$tab]" s *= "$tab]"
s
else else
assets_string = "[]" "[]"
end end
text = """
make = """
using Documenter, $pkg_name using Documenter, $pkg_name
makedocs(; makedocs(;
@ -41,21 +41,25 @@ function gen_plugin(
pages=[ pages=[
"Home" => "index.md", "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", sitename="$pkg_name.jl",
authors="$(template.authors)", authors="$(t.authors)",
assets=$assets_string, assets=$assets_string,
) )
""" """
docs = """
# $pkg_name.jl
gen_file(joinpath(dirname(docs_dir), "make.jl"), text) ```@index
open(joinpath(docs_dir, "index.md"), "w") do fp ```
write(fp, "# $pkg_name")
end ```@autodocs
readme_path = joinpath(dir, pkg_name, "README.md") Modules = [$pkg_name]
if isfile(readme_path) ```
cp(readme_path, joinpath(docs_dir, "index.md"), force=true) """
end
gen_file(joinpath(dirname(docs_dir), "make.jl"), make)
gen_file(joinpath(docs_dir, "index.md"), docs)
end end
function Base.show(io::IO, p::Documenter) 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), ", "))") n > 0 && print(io, ": $(join(map(g -> "\"$g\"", p.gitignore), ", "))")
end end
function interactive(plugin_type::Type{<:Documenter}) function interactive(t::Type{<:Documenter})
t = nameof(plugin_type) name = string(nameof(t))
print("$t: Enter any Documenter asset files (separated by spaces) []: ") print("$name: Enter any Documenter asset files (separated by spaces) []: ")
return plugin_type(; assets=String.(split(readline()))) return t(; assets=string.(split(readline())))
end end

View File

@ -38,27 +38,20 @@ function badges(::GitHubPages, user::AbstractString, pkg_name::AbstractString)
] ]
end end
function gen_plugin( function gen_plugin(p::GitHubPages, t::Template, pkg_name::AbstractString)
plugin::GitHubPages, invoke(gen_plugin, Tuple{Documenter, Template, AbstractString}, p, t, pkg_name)
template::Template,
dir::AbstractString, if haskey(t.plugins, TravisCI)
pkg_name::AbstractString, docs_src = joinpath(t.dir, pkg_name, "docs", "src")
)
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")
open(joinpath(dirname(docs_src), "make.jl"), "a") do file open(joinpath(dirname(docs_src), "make.jl"), "a") do file
write( write(
file, file,
""" """
deploydocs(; deploydocs(;
repo="github.com/$(template.user)/$pkg_name.jl", repo="$(t.host)/$(t.user)/$pkg_name.jl",
target="build", target="build",
julia="0.6", julia="1.0",
deps=nothing, deps=nothing,
make=nothing, make=nothing,
) )

View File

@ -55,11 +55,11 @@ generated repositories, and appropriate badge(s) to the README.
end end
end end
function interactive(plugin_type::Type{GitLabCI}) function interactive(::Type{GitLabCI})
name = "GitLabCI" name = "GitLabCI"
kwargs = Dict{Symbol, Any}() kwargs = Dict{Symbol, Any}()
default_config_file = joinpath(DEFAULTS_DIR, "gitlab-ci.yml") default_config_file = joinpath(DEFAULTS_DIR, "gitlab-ci.yml")
print("$name: Enter the config template filename (\"None\" for no file) ") print("$name: Enter the config template filename (\"None\" for no file) ")
print("[$default_config_file]: ") print("[$default_config_file]: ")
config_file = readline() config_file = readline()

View File

@ -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. create a template, you can use [`interactive_template`](@ref) instead.
# Keyword Arguments # Keyword Arguments
* `user::AbstractString=""`: GitHub username. If left unset, it will try to take the * `user::AbstractString=""`: GitHub (or other code hosting service) username. If left
value of a supplied git config's "github.user" key, then the global git config's unset, it will take the the global git config's value. If that is not set, an
value. If neither is set, an `ArgumentError` is thrown. `ArgumentError` is thrown. **This is case-sensitive for some plugins, so take care to
**This is case-sensitive for some plugins, so take care to enter it correctly.** enter it correctly.**
* `host::AbstractString="github.com"`: URL to the code hosting service where your package * `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 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. 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 * `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 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 available licenses, and [`show_license`](@ref) can be used to print out a particular
license's text. license's text.
* `authors::Union{AbstractString, Vector{<:AbstractString}}=""`: Names that appear on the * `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`, 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 it will take the value of of the global git config's value if it is left unset.
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".
* `dir::AbstractString=$(dev_dir())`: Directory in which the package will go. Relative * `dir::AbstractString=$(dev_dir())`: Directory in which the package will go. Relative
paths are converted to absolute ones at template creation time. 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. * `julia_version::VersionNumber=VERSION`: Minimum allowed Julia version.
* `requirements::Vector{<:AbstractString}=String[]`: Package requirements. If there are * `ssh::Bool=false`: Whether or not to use SSH for the remote.
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.
* `plugins::Vector{<:Plugin}=Plugin[]`: A list of `Plugin`s that the package will include. * `plugins::Vector{<:Plugin}=Plugin[]`: A list of `Plugin`s that the package will include.
""" """
@auto_hash_equals struct Template @auto_hash_equals struct Template
@ -47,12 +37,9 @@ create a template, you can use [`interactive_template`](@ref) instead.
host::AbstractString host::AbstractString
license::AbstractString license::AbstractString
authors::AbstractString authors::AbstractString
years::AbstractString
dir::AbstractString dir::AbstractString
precompile::Bool
julia_version::VersionNumber julia_version::VersionNumber
requirements::Vector{AbstractString} ssh::Bool
gitconfig::Dict
plugins::Dict{DataType, Plugin} plugins::Dict{DataType, Plugin}
function Template(; function Template(;
@ -60,18 +47,24 @@ create a template, you can use [`interactive_template`](@ref) instead.
host::AbstractString="https://github.com", host::AbstractString="https://github.com",
license::AbstractString="MIT", license::AbstractString="MIT",
authors::Union{AbstractString, Vector{<:AbstractString}}="", authors::Union{AbstractString, Vector{<:AbstractString}}="",
years::Union{Integer, AbstractString}=Dates.year(Dates.today()),
dir::AbstractString=dev_dir(), dir::AbstractString=dev_dir(),
precompile::Bool=true,
julia_version::VersionNumber=VERSION, julia_version::VersionNumber=VERSION,
requirements::Vector{<:AbstractString}=String[], ssh::Bool=false,
gitconfig::Dict=Dict(),
plugins::Vector{<:Plugin}=Plugin[], plugins::Vector{<:Plugin}=Plugin[],
) )
# If no username was set, look for one in a supplied git config, # Check for required Git options for package generation
# and then in the global git config. # (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) if isempty(user)
user = get(gitconfig, "github.user", LibGit2.getconfig("github.user", "")) user = LibGit2.getconfig("github.user", "")
end end
if isempty(user) if isempty(user)
throw(ArgumentError("No GitHub username found, set one with user=username")) 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")) throw(ArgumentError("License '$license' is not available"))
end end
# If no author was set, look for one in the supplied git config, # If no author was set, look for one in the global git config.
# and then in the global git config.
if isempty(authors) if isempty(authors)
authors = get(gitconfig, "user.name", LibGit2.getconfig("user.name", "")) authors = LibGit2.getconfig("user.name", "")
elseif isa(authors, Vector) elseif isa(authors, Vector)
authors = join(authors, ", ") authors = join(authors, ", ")
end end
years = string(years)
dir = abspath(expanduser(dir)) 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) plugin_dict = Dict{DataType, Plugin}(typeof(p) => p for p in plugins)
if (length(plugins) != length(plugin_dict)) if (length(plugins) != length(plugin_dict))
@warn "Plugin list contained duplicates, only the last of each type was kept" @warn "Plugin list contained duplicates, only the last of each type was kept"
end end
new( new(user, host, license, authors, dir, julia_version, ssh, plugin_dict)
user, host, license, authors, years, dir, precompile,
julia_version, requirements_dedup, gitconfig, plugin_dict,
)
end end
end end
function Base.show(io::IO, t::Template) 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 = " " spc = " "
println(io, "Template:") println(io, "Template:")
println(io, "$spc→ User: $(maybe_none(t.user))") println(io, "$spc→ User: $(maybe(t.user))")
println(io, "$spc→ Host: $(maybe_none(t.host))") println(io, "$spc→ Host: $(maybe(t.host))")
print(io, "$spc→ License: ") print(io, "$spc→ License: ")
if isempty(t.license) if isempty(t.license)
println(io, "None") println(io, "None")
else else
println(io, "$(t.license) ($(t.authors) $(t.years))") println(io, "$(t.license) ($(t.authors) $(Dates.year(Dates.now())))")
end end
println(io, "$spc→ Package directory: $(replace(maybe_none(t.dir), homedir() => "~"))") println(io, "$spc→ Package directory: $(replace(maybe(t.dir), homedir() => "~"))")
println(io, "$spc→ Precompilation enabled: $(t.precompile ? "Yes" : "No")")
println(io, "$spc→ Minimum Julia version: v$(version_floor(t.julia_version))") println(io, "$spc→ Minimum Julia version: v$(version_floor(t.julia_version))")
println(io, "$spc→ SSH remote: $(t.ssh ? "Yes" : "No")")
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
print(io, "$spc→ Plugins:") print(io, "$spc→ Plugins:")
if isempty(t.plugins) if isempty(t.plugins)
@ -214,12 +170,11 @@ function interactive_template(; fast::Bool=false)
menu = RadioMenu(String["None", split(String(take!(io)), "\n")...]) 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 # If the user breaks out of the menu with Ctrl-c, the result is -1, the absolute
# value of which correponds to no license. # value of which correponds to no license.
licenses[abs(request(menu))].first first(licenses[abs(request(menu))])
end 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. # because the license is the only place that they matter.
kwargs[:authors] = if fast || isempty(kwargs[:license]) kwargs[:authors] = if fast || isempty(kwargs[:license])
LibGit2.getconfig("user.name", "") LibGit2.getconfig("user.name", "")
else else
@ -230,15 +185,6 @@ function interactive_template(; fast::Bool=false)
isempty(authors) ? default_authors : authors isempty(authors) ? default_authors : authors
end 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 kwargs[:dir] = if fast
dev_dir() dev_dir()
else else
@ -248,13 +194,6 @@ function interactive_template(; fast::Bool=false)
isempty(dir) ? default_dir : dir isempty(dir) ? default_dir : dir
end end
kwargs[:precompile] = if fast
true
else
print("Enable precompilation? [yes]: ")
!in(uppercase(readline()), ["N", "NO", "F", "FALSE"])
end
kwargs[:julia_version] = if fast kwargs[:julia_version] = if fast
VERSION VERSION
else else
@ -264,28 +203,11 @@ function interactive_template(; fast::Bool=false)
isempty(julia_version) ? default_julia_version : VersionNumber(julia_version) isempty(julia_version) ? default_julia_version : VersionNumber(julia_version)
end end
kwargs[:requirements] = if fast kwargs[:ssh] = if fast
String[] false
else else
print("Enter any Julia package requirements, (separated by spaces) []: ") print("Set remote to SSH? [no]: ")
String.(split(readline())) in(uppercase(readline()), ["Y", "YES", "T", "TRUE"])
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
end end
println("Select plugins:") println("Select plugins:")

View File

@ -7,17 +7,15 @@
# Therefore, we skip any interactive tests on OSX builds. # Therefore, we skip any interactive tests on OSX builds.
@testset "Interactive template creation" begin @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() t = interactive_template()
@test t.user == me @test t.user == me
@test t.host == "github.com" @test t.host == "github.com"
@test isempty(t.license) @test isempty(t.license)
@test t.authors == LibGit2.getconfig("user.name", "") @test t.authors == LibGit2.getconfig("user.name", "")
@test t.years == string(Dates.year(Dates.today()))
@test t.dir == default_dir @test t.dir == default_dir
@test t.julia_version == VERSION @test t.julia_version == VERSION
@test isempty(t.requirements) @test !t.ssh
@test isempty(t.gitconfig)
@test isempty(t.plugins) @test isempty(t.plugins)
if isempty(LibGit2.getconfig("github.user", "")) if isempty(LibGit2.getconfig("github.user", ""))
@ -25,44 +23,35 @@
@test_throws ArgumentError t = interactive_template() @test_throws ArgumentError t = interactive_template()
end 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() t = interactive_template()
@test t.user == me @test t.user == me
@test t.host == "gitlab.com" @test t.host == "gitlab.com"
# Not sure if the order the licenses are displayed in is consistent. # Not sure if the order the licenses are displayed in is consistent.
@test !isempty(t.license) @test !isempty(t.license)
@test t.authors == me @test t.authors == me
@test t.years == "2016"
@test t.dir == abspath(test_file) @test t.dir == abspath(test_file)
@test !t.precompile
@test t.julia_version == v"0.5.0" @test t.julia_version == v"0.5.0"
@test Set(t.requirements) == Set(["X", "Y"]) @test t.ssh
@test t.gitconfig == Dict("key" => "val val", "key2" => "val2")
# Like above, not sure which plugins this will generate. # Like above, not sure which plugins this will generate.
@test length(t.plugins) == 2 @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") write(stdin.buffer, "$me\nd")
t = interactive_template(; fast=true) t = interactive_template(; fast=true)
@test t.user == me @test t.user == me
@test t.host == "github.com" @test t.host == "github.com"
@test t.license == "MIT" @test t.license == "MIT"
@test t.authors == LibGit2.getconfig("user.name", "") @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.dir == default_dir
@test t.julia_version == VERSION @test t.julia_version == VERSION
@test isempty(t.requirements) @test !t.ssh
@test isempty(t.gitconfig)
@test isempty(t.plugins) @test isempty(t.plugins)
println() println()
end end
@testset "Interactive package generation" begin @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\n\nd")
write(stdin.buffer, "$me\n\n\r\n\n\n\n$cfg\n\nd")
generate_interactive(test_pkg) generate_interactive(test_pkg)
@test isdir(joinpath(default_dir, test_pkg)) @test isdir(joinpath(default_dir, test_pkg))
rm(joinpath(default_dir, test_pkg); force=true, recursive=true) rm(joinpath(default_dir, test_pkg); force=true, recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "AppVeyor" begin @testset "AppVeyor" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -26,47 +24,34 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = AppVeyor() 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 end
@testset "File generation" begin @testset "File generation" begin
# Without a coverage plugin in the template, there should be no post-test step. # Without a coverage plugin in the template, there should be no post-test step.
p = AppVeyor() 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")) @test isfile(joinpath(pkg_dir, ".appveyor.yml"))
appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String) appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String)
@test !occursin("coverage=true", appveyor) @test !occursin("on_success", appveyor)
@test !occursin("after_test", appveyor) @test !occursin("%JL_CODECOV_SCRIPT%", appveyor)
@test !occursin("Codecov.submit", appveyor)
@test !occursin("Coveralls.submit", appveyor)
rm(joinpath(pkg_dir, ".appveyor.yml")) rm(joinpath(pkg_dir, ".appveyor.yml"))
# Generating the plugin with CodeCov in the template should create a post-test step. # Generating the plugin with CodeCov in the template should create a post-test step.
t.plugins[CodeCov] = CodeCov() t.plugins[CodeCov] = CodeCov()
gen_plugin(p, t, temp_dir, test_pkg) gen_plugin(p, t, test_pkg)
delete!(t.plugins, CodeCov) delete!(t.plugins, CodeCov)
appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String) appveyor = read(joinpath(pkg_dir, ".appveyor.yml"), String)
@test occursin("coverage=true", appveyor) @test occursin("on_success", appveyor)
@test occursin("after_test", appveyor) @test occursin("%JL_CODECOV_SCRIPT%", appveyor)
@test occursin("Codecov.submit", appveyor)
@test !occursin("Coveralls.submit", appveyor)
rm(joinpath(pkg_dir, ".appveyor.yml")) rm(joinpath(pkg_dir, ".appveyor.yml"))
# Coveralls should do the same. # TODO: Add Coveralls tests when AppVeyor.jl supports it.
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"))
p = AppVeyor(; config_file=nothing) 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")) @test !isfile(joinpath(pkg_dir, ".appveyor.yml"))
end end
end end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "CodeCov" begin @testset "CodeCov" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -26,17 +24,18 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = CodeCov() 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 end
@testset "File generation" begin @testset "File generation" begin
p = CodeCov() p = CodeCov()
@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=nothing)
@test isempty(gen_plugin(p, t, temp_dir, test_pkg))
@test !isfile(joinpath(pkg_dir, ".codecov.yml")) @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
end end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Coveralls" begin @testset "Coveralls" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -26,18 +24,17 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = Coveralls() 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 end
@testset "File generation" begin @testset "File generation" begin
p = Coveralls() 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")) @test !isfile(joinpath(pkg_dir, ".coveralls.yml"))
p = Coveralls(; config_file=test_file) 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")) @test isfile(joinpath(pkg_dir, ".coveralls.yml"))
rm(joinpath(pkg_dir, ".coveralls.yml"))
end end
end end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "GitHubPages" begin @testset "GitHubPages" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -15,15 +13,15 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = GitHubPages() p = GitHubPages()
@test badges(p, user, test_pkg) == [ @test badges(p, me, test_pkg) == [
"[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://$user.github.io/$test_pkg.jl/stable)" "[![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://$user.github.io/$test_pkg.jl/latest)" "[![Latest](https://img.shields.io/badge/docs-latest-blue.svg)](https://$me.github.io/$test_pkg.jl/latest)"
] ]
end end
@testset "File generation" begin @testset "File generation" begin
p = GitHubPages() 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 isdir(joinpath(pkg_dir, "docs"))
@test isfile(joinpath(pkg_dir, "docs", "make.jl")) @test isfile(joinpath(pkg_dir, "docs", "make.jl"))
make = readchomp(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 isdir(joinpath(pkg_dir, "docs", "src"))
@test isfile(joinpath(pkg_dir, "docs", "src", "index.md")) @test isfile(joinpath(pkg_dir, "docs", "src", "index.md"))
index = readchomp(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) rm(joinpath(pkg_dir, "docs"); recursive=true)
p = GitHubPages(; assets=[test_file]) 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")) make = readchomp(joinpath(pkg_dir, "docs", "make.jl"))
# Check the formatting of the assets list. # Check the formatting of the assets list.
@test occursin( @test occursin(
@ -49,11 +47,22 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@test isfile(joinpath(pkg_dir, "docs", "src", "assets", basename(test_file))) @test isfile(joinpath(pkg_dir, "docs", "src", "assets", basename(test_file)))
rm(joinpath(pkg_dir, "docs"); recursive=true) rm(joinpath(pkg_dir, "docs"); recursive=true)
t.plugins[TravisCI] = TravisCI() 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")) make = readchomp(joinpath(pkg_dir, "docs", "make.jl"))
@test occursin("deploydocs", make) @test occursin("deploydocs", make)
rm(joinpath(pkg_dir, "docs"); recursive=true) rm(joinpath(pkg_dir, "docs"); recursive=true)
end 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 end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "GitLabCI" begin @testset "GitLabCI" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -40,33 +38,32 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = GitLabCI() p = GitLabCI()
@test badges(p, user, test_pkg) == [ @test badges(p, me, test_pkg) == [
"[![Build Status](https://gitlab.com/$user/$test_pkg.jl/badges/master/build.svg)](https://gitlab.com/$user/$test_pkg.jl/pipelines)", "[![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/$user/$test_pkg.jl/badges/master/coverage.svg)](https://gitlab.com/$user/$test_pkg.jl/commits/master)", "[![Coverage](https://gitlab.com/$me/$test_pkg.jl/badges/master/coverage.svg)](https://gitlab.com/$me/$test_pkg.jl/commits/master)",
] ]
end end
@testset "File generation" begin @testset "File generation" begin
p = GitLabCI() 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")) @test isfile(joinpath(pkg_dir, ".gitlab-ci.yml"))
gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String) gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String)
@test occursin("test_template", gitlab)
# The default plugin should enable the coverage step. # The default plugin should enable the coverage step.
@test occursin("using Coverage", gitlab) @test occursin("using Coverage", gitlab)
rm(joinpath(pkg_dir, ".gitlab-ci.yml")) rm(joinpath(pkg_dir, ".gitlab-ci.yml"))
p = GitLabCI(; coverage=false) 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) gitlab = read(joinpath(pkg_dir, ".gitlab-ci.yml"), String)
# If coverage is false, there should be no coverage step. # If coverage is false, there should be no coverage step.
@test !occursin("using Coverage", gitlab) @test !occursin("using Coverage", gitlab)
rm(joinpath(pkg_dir, ".gitlab-ci.yml")) rm(joinpath(pkg_dir, ".gitlab-ci.yml"))
p = GitLabCI(; config_file=nothing) 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")) @test !isfile(joinpath(pkg_dir, ".gitlab-ci.yml"))
end end
end end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -1,7 +1,5 @@
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
@testset "TravisCI" begin @testset "TravisCI" begin
@testset "Plugin creation" begin @testset "Plugin creation" begin
@ -26,13 +24,13 @@ pkg_dir = joinpath(temp_dir, test_pkg)
@testset "Badge generation" begin @testset "Badge generation" begin
p = TravisCI() 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 end
@testset "File generation" begin @testset "File generation" begin
# Without a coverage plugin in the template, there should be no post-test step. # Without a coverage plugin in the template, there should be no post-test step.
p = TravisCI() 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")) @test isfile(joinpath(pkg_dir, ".travis.yml"))
travis = read(joinpath(pkg_dir, ".travis.yml"), String) 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. # Generating the plugin with CodeCov in the template should create a post-test step.
t.plugins[CodeCov] = CodeCov() t.plugins[CodeCov] = CodeCov()
gen_plugin(p, t, temp_dir, test_pkg) gen_plugin(p, t, test_pkg)
delete!(t.plugins, CodeCov) delete!(t.plugins, CodeCov)
travis = read(joinpath(pkg_dir, ".travis.yml"), String) travis = read(joinpath(pkg_dir, ".travis.yml"), String)
@test occursin("after_success", travis) @test occursin("after_success", travis)
@ -55,7 +53,7 @@ pkg_dir = joinpath(temp_dir, test_pkg)
# Coveralls should do the same. # Coveralls should do the same.
t.plugins[Coveralls] = Coveralls() t.plugins[Coveralls] = Coveralls()
gen_plugin(p, t, temp_dir, test_pkg) gen_plugin(p, t, test_pkg)
delete!(t.plugins, Coveralls) delete!(t.plugins, Coveralls)
travis = read(joinpath(pkg_dir, ".travis.yml"), String) travis = read(joinpath(pkg_dir, ".travis.yml"), String)
@test occursin("after_success", travis) @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. # With a Documenter plugin, there should be a docs deployment step.
t.plugins[GitHubPages] = GitHubPages() t.plugins[GitHubPages] = GitHubPages()
gen_plugin(p, t, temp_dir, test_pkg) gen_plugin(p, t, test_pkg)
delete!(t.plugins, GitHubPages) delete!(t.plugins, GitHubPages)
travis = read(joinpath(pkg_dir, ".travis.yml"), String) travis = read(joinpath(pkg_dir, ".travis.yml"), String)
@test occursin("after_success", travis) @test occursin("after_success", travis)
@ -76,9 +74,9 @@ pkg_dir = joinpath(temp_dir, test_pkg)
rm(joinpath(pkg_dir, ".travis.yml")) rm(joinpath(pkg_dir, ".travis.yml"))
p = TravisCI(; config_file=nothing) 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")) @test !isfile(joinpath(pkg_dir, ".travis.yml"))
end end
end end
rm(temp_dir; recursive=true) rm(pkg_dir; recursive=true)

View File

@ -4,9 +4,9 @@ using Dates
using LibGit2 using LibGit2
import PkgTemplates: badges, version_floor, substitute, read_license, gen_file, gen_readme, import PkgTemplates: badges, version_floor, substitute, read_license, gen_file, gen_readme,
gen_tests, gen_license, gen_require, gen_entrypoint, gen_gitignore, gen_plugin, gen_tests, gen_license, gen_require, gen_gitignore, gen_plugin, show_license, LICENSES,
show_license, LICENSES, LICENSE_DIR, Plugin, GenericPlugin, CustomPlugin, Badge, LICENSE_DIR, Plugin, GenericPlugin, CustomPlugin, Badge, format, interactive,
format, interactive, DEFAULTS_DIR DEFAULTS_DIR
mktempdir() do temp_dir mktempdir() do temp_dir
mkdir(joinpath(temp_dir, "dev")) mkdir(joinpath(temp_dir, "dev"))

View File

@ -16,13 +16,8 @@ struct Baz <: Plugin end
# Various options to be passed into templates. # Various options to be passed into templates.
const me = "christopher-dG" const me = "christopher-dG"
const gitconfig = Dict(
"user.name" => "Tester McTestFace",
"user.email" => "email@web.site",
"github.user" => "TesterMcTestFace",
)
const test_pkg = "TestPkg" 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 test_file = tempname()
const default_dir = PkgTemplates.dev_dir() const default_dir = PkgTemplates.dev_dir()
const template_text = """ const template_text = """
@ -41,11 +36,9 @@ write(test_file, template_text)
t = Template(; user=me) t = Template(; user=me)
@test t.user == me @test t.user == me
@test t.license == "MIT" @test t.license == "MIT"
@test t.years == string(Dates.year(Dates.today()))
@test t.authors == LibGit2.getconfig("user.name", "") @test t.authors == LibGit2.getconfig("user.name", "")
@test t.dir == default_dir @test t.dir == default_dir
@test t.julia_version == VERSION @test t.julia_version == VERSION
@test isempty(t.gitconfig)
@test isempty(t.plugins) @test isempty(t.plugins)
# Checking non-default field assignments. # Checking non-default field assignments.
@ -56,14 +49,8 @@ write(test_file, template_text)
t = Template(; user=me, license="MPL") t = Template(; user=me, license="MPL")
@test t.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") t = Template(; user=me, authors="Some Guy")
@test t.authors == "Some Guy" @test t.authors == "Some Guy"
# Vectors of authors should be comma-joined. # Vectors of authors should be comma-joined.
t = Template(; user=me, authors=["Guy", "Gal"]) t = Template(; user=me, authors=["Guy", "Gal"])
@test t.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)) @test t.dir == joinpath(homedir(), basename(test_file))
end end
t = Template(; user=me, precompile=false)
@test !t.precompile
t = Template(; user=me, julia_version=v"0.1.2") t = Template(; user=me, julia_version=v"0.1.2")
@test t.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. # The template should contain whatever plugins you give it.
t = Template(; t = Template(;
user=me, user=me,
@ -147,30 +107,24 @@ end
@testset "Show methods" begin @testset "Show methods" begin
pkg_dir = replace(default_dir, homedir() => "~") pkg_dir = replace(default_dir, homedir() => "~")
buf = IOBuffer() buf = IOBuffer()
t = Template(; user=me, gitconfig=gitconfig) t = Template(; user=me)
show(buf, t) show(buf, t)
text = String(take!(buf)) text = String(take!(buf))
expected = """ expected = """
Template: Template:
User: $me User: $me
Host: github.com Host: github.com
License: MIT ($(gitconfig["user.name"]) $(Dates.year(now()))) License: MIT ($(LibGit2.getconfig("user.name", "")) $(Dates.year(now())))
Package directory: $pkg_dir Package directory: $pkg_dir
Precompilation enabled: Yes
Minimum Julia version: v$(PkgTemplates.version_floor()) Minimum Julia version: v$(PkgTemplates.version_floor())
0 package requirements SSH remote: No
Git configuration options:
github.user = $(gitconfig["github.user"])
user.email = $(gitconfig["user.email"])
user.name = $(gitconfig["user.name"])
Plugins: None Plugins: None
""" """
@test text == rstrip(expected) @test text == rstrip(expected)
t = Template( t = Template(
user=me, user=me,
license="", license="",
requirements=["Foo", "Bar"], ssh=true,
gitconfig=gitconfig,
plugins=[ plugins=[
TravisCI(), TravisCI(),
CodeCov(), CodeCov(),
@ -185,13 +139,8 @@ end
Host: github.com Host: github.com
License: None License: None
Package directory: $pkg_dir Package directory: $pkg_dir
Precompilation enabled: Yes
Minimum Julia version: v$(PkgTemplates.version_floor()) Minimum Julia version: v$(PkgTemplates.version_floor())
2 package requirements: Bar, Foo SSH remote: Yes
Git configuration options:
github.user = $(gitconfig["github.user"])
user.email = $(gitconfig["user.email"])
user.name = $(gitconfig["user.name"])
Plugins: Plugins:
CodeCov: CodeCov:
Config file: None Config file: None
@ -210,8 +159,6 @@ end
t = Template(; t = Template(;
user=me, user=me,
license="MPL", license="MPL",
requirements=[test_pkg],
gitconfig=gitconfig,
plugins=[Coveralls(), TravisCI(), CodeCov(), GitHubPages(), AppVeyor()], plugins=[Coveralls(), TravisCI(), CodeCov(), GitHubPages(), AppVeyor()],
) )
temp_dir = mktempdir() temp_dir = mktempdir()
@ -224,7 +171,7 @@ end
rm(temp_file) rm(temp_file)
# Test the README generation. # 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")) @test isfile(joinpath(pkg_dir, "README.md"))
readme = readchomp(joinpath(pkg_dir, "README.md")) readme = readchomp(joinpath(pkg_dir, "README.md"))
rm(joinpath(pkg_dir, "README.md")) rm(joinpath(pkg_dir, "README.md"))
@ -240,7 +187,7 @@ end
something(findfirst("coveralls", readme)).start something(findfirst("coveralls", readme)).start
# Plugins with badges but not in BADGE_ORDER should appear at the far right side. # Plugins with badges but not in BADGE_ORDER should appear at the far right side.
t.plugins[Foo] = Foo() t.plugins[Foo] = Foo()
gen_readme(temp_dir, test_pkg, t) gen_readme(pkg_dir, t)
readme = readchomp(joinpath(pkg_dir, "README.md")) readme = readchomp(joinpath(pkg_dir, "README.md"))
rm(joinpath(pkg_dir, "README.md")) rm(joinpath(pkg_dir, "README.md"))
@test <( @test <(
@ -249,7 +196,7 @@ end
) )
# Test the gitignore generation. # 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")) @test isfile(joinpath(pkg_dir, ".gitignore"))
gitignore = read(joinpath(pkg_dir, ".gitignore"), String) gitignore = read(joinpath(pkg_dir, ".gitignore"), String)
rm(joinpath(pkg_dir, ".gitignore")) rm(joinpath(pkg_dir, ".gitignore"))
@ -261,50 +208,37 @@ end
end end
# Test the license generation. # 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")) @test isfile(joinpath(pkg_dir, "LICENSE"))
license = readchomp(joinpath(pkg_dir, "LICENSE")) license = readchomp(joinpath(pkg_dir, "LICENSE"))
rm(joinpath(pkg_dir, "LICENSE")) rm(joinpath(pkg_dir, "LICENSE"))
@test occursin(t.authors, license) @test occursin(t.authors, license)
@test occursin(t.years, license)
@test occursin(read_license(t.license), 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 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")) @test isfile(joinpath(pkg_dir, "REQUIRE"))
vf = version_floor(t.julia_version) 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")) rm(joinpath(pkg_dir, "REQUIRE"))
# Test the test generation. # 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 isdir(joinpath(pkg_dir, "test"))
@test isfile(joinpath(pkg_dir, "test", "runtests.jl")) @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) rm(joinpath(pkg_dir, "test"); recursive=true)
@test occursin("using $test_pkg", runtests) @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) rm(temp_dir; recursive=true)
end end
@testset "Package generation" begin @testset "Package generation" begin
t = Template(; user=me, gitconfig=gitconfig) t = Template(; user=me)
generate(test_pkg, t) generate(test_pkg, t)
pkg_dir = joinpath(default_dir, test_pkg) pkg_dir = joinpath(default_dir, test_pkg)
@ -315,31 +249,31 @@ end
@test isfile(joinpath(pkg_dir, ".gitignore")) @test isfile(joinpath(pkg_dir, ".gitignore"))
@test isdir(joinpath(pkg_dir, "src")) @test isdir(joinpath(pkg_dir, "src"))
@test isfile(joinpath(pkg_dir, "src", "$test_pkg.jl")) @test isfile(joinpath(pkg_dir, "src", "$test_pkg.jl"))
@test isfile(joinpath(pkg_dir, "Project.toml"))
@test isdir(joinpath(pkg_dir, "test")) @test isdir(joinpath(pkg_dir, "test"))
@test isfile(joinpath(pkg_dir, "test", "runtests.jl")) @test isfile(joinpath(pkg_dir, "test", "runtests.jl"))
# Check the gitconfig. @test isfile(joinpath(pkg_dir, "Manifest.toml"))
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"]
# Check the configured remote and branches. # Check the configured remote and branches.
# Note: This test will fail on your system if you've configured Git # Note: This test will fail on your system if you've configured Git
# to replace all HTTPS URLs with SSH. # 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 LibGit2.url(remote) == "https://github.com/$me/$test_pkg.jl"
@test in("master", branches) @test branches == ["master"]
@test !in("gh-pages", branches)
@test !LibGit2.isdirty(repo) @test !LibGit2.isdirty(repo)
rm(pkg_dir; recursive=true) rm(pkg_dir; recursive=true)
# Check that the remote is an SSH URL. # Check that the remote is an SSH URL when we want it to be.
generate(t, test_pkg; ssh=true) # Test the reversed-arguments method. t = Template(; user=me, ssh=true)
generate(t, test_pkg) # Test the reversed-arguments method here.
repo = LibGit2.GitRepo(pkg_dir) repo = LibGit2.GitRepo(pkg_dir)
remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") remote = LibGit2.get(LibGit2.GitRemote, repo, "origin")
@test LibGit2.url(remote) == "git@github.com:$me/$test_pkg.jl.git" @test LibGit2.url(remote) == "git@github.com:$me/$test_pkg.jl.git"
rm(pkg_dir; recursive=true) rm(pkg_dir; recursive=true)
# Check that the remote is set correctly for non-default hosts. # 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) generate(test_pkg, t)
repo = LibGit2.GitRepo(pkg_dir) repo = LibGit2.GitRepo(pkg_dir)
remote = LibGit2.get(LibGit2.GitRemote, repo, "origin") remote = LibGit2.get(LibGit2.GitRemote, repo, "origin")
@ -348,62 +282,10 @@ end
# Check that the package ends up in the right directory. # Check that the package ends up in the right directory.
temp_dir = mktempdir() temp_dir = mktempdir()
t = Template(; user=me, dir=temp_dir, gitconfig=gitconfig) t = Template(; user=me, dir=temp_dir)
generate(test_pkg, t) generate(test_pkg, t)
@test isdir(joinpath(temp_dir, test_pkg)) @test isdir(joinpath(temp_dir, test_pkg))
rm(temp_dir; recursive=true) 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 end
@testset "Version floor" begin @testset "Version floor" begin
@ -485,10 +367,8 @@ end
end end
@testset "Plugins" begin @testset "Plugins" begin
user = gitconfig["github.user"]
t = Template(; user=me) t = Template(; user=me)
temp_dir = mktempdir() pkg_dir = joinpath(t.dir, test_pkg)
pkg_dir = joinpath(temp_dir, test_pkg)
# Check badge constructor and formatting. # Check badge constructor and formatting.
badge = Badge("A", "B", "C") badge = Badge("A", "B", "C")
@ -498,12 +378,12 @@ end
@test format(badge) == "[![A](B)](C)" @test format(badge) == "[![A](B)](C)"
p = Bar() p = Bar()
@test isempty(badges(p, user, test_pkg)) @test isempty(badges(p, me, test_pkg))
@test isempty(gen_plugin(p, t, temp_dir, test_pkg)) @test isempty(gen_plugin(p, t, test_pkg))
p = Baz() p = Baz()
@test isempty(badges(p, user, test_pkg)) @test isempty(badges(p, me, test_pkg))
@test isempty(gen_plugin(p, t, temp_dir, test_pkg)) @test isempty(gen_plugin(p, t, test_pkg))
include(joinpath("plugins", "travisci.jl")) include(joinpath("plugins", "travisci.jl"))
include(joinpath("plugins", "appveyor.jl")) include(joinpath("plugins", "appveyor.jl"))
@ -511,8 +391,6 @@ end
include(joinpath("plugins", "codecov.jl")) include(joinpath("plugins", "codecov.jl"))
include(joinpath("plugins", "coveralls.jl")) include(joinpath("plugins", "coveralls.jl"))
include(joinpath("plugins", "githubpages.jl")) include(joinpath("plugins", "githubpages.jl"))
rm(temp_dir; recursive=true)
end end
rm(test_file) rm(test_file)