diff --git a/dev/assets/documenter.css b/dev/assets/documenter.css index 7cd2662..d1f8049 100644 --- a/dev/assets/documenter.css +++ b/dev/assets/documenter.css @@ -83,6 +83,10 @@ img { max-width: 100%; } +video { + max-width: 100%; +} + table { border-collapse: collapse; margin: 1em 0; diff --git a/dev/assets/search.js b/dev/assets/search.js index f99b378..5d32c3a 100644 --- a/dev/assets/search.js +++ b/dev/assets/search.js @@ -39,8 +39,8 @@ parseUri.options = { requirejs.config({ paths: { 'jquery': 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min', - 'lunr': 'https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.1/lunr.min', - 'lodash': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min', + 'lunr': 'https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.5/lunr.min', + 'lodash': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min', } }); diff --git a/dev/developer/index.html b/dev/developer/index.html new file mode 100644 index 0000000..7039bd6 --- /dev/null +++ b/dev/developer/index.html @@ -0,0 +1,127 @@ + +Developer Guide · PkgTemplates.jl

Developer Guide

PkgTemplates Developer Guide

PkgTemplates can be easily extended by adding new Plugins.

There are two types of plugins: Plugin and BasicPlugin.

Plugins are PkgTemplates' source of customization and extensibility. Add plugins to your Templates to enable extra pieces of repository setup.

When implementing a new plugin, subtype this type to have full control over its behaviour.

source

A simple plugin that, in general, creates a single file.

source

Template + Package Creation Pipeline

The Template constructor basically does this:

- extract values from keyword arguments
+- create a Template from the values
+- for each plugin:
+  - validate plugin against the template

The plugin validation step uses the validate function. It lets us catch mistakes before we try to generate packages.

PkgTemplates.validateFunction.
validate(::Plugin, ::Template)

Perform any required validation for a Plugin.

It is preferred to do validation here instead of in prehook, because this function is called at Template construction time, whereas the prehook is only run at package generation time.

source

The package generation process looks like this:

- create empty directory for the package
+- for each plugin, ordered by priority:
+  - run plugin prehook
+- for each plugin, ordered by priority:
+  - run plugin hook
+- for each plugin, ordered by priority:
+  - run plugin posthook

As you can tell, plugins play a central role in setting up a package.

The three main entrypoints for plugins to do work are the prehook, the hook, and the posthook. As the names might imply, they basically mean "before the main stage", "the main stage", and "after the main stage", respectively.

Each stage is basically identical, since the functions take the exact same arguments. However, the multiple stages allow us to depend on artifacts of the previous stages. For example, the Git plugin uses posthook to commit all generated files, but it wouldn't make sense to do that before the files are generated.

But what about dependencies within the same stage? In this case, we have priority to define which plugins go when. The Git plugin also uses this function to lower its posthook's priority, so that even if other plugins generate files in their posthooks, they still get committed (provided that those plugins didn't set an even lower priority).

PkgTemplates.prehookFunction.
prehook(::Plugin, ::Template, pkg_dir::AbstractString)

Stage 1 of the package generation process (the "before" stage, in general). At this point, pkg_dir is an empty directory that will eventually contain the package, and neither the hooks nor the posthooks have run.

Note

pkg_dir only stays empty until the first plugin chooses to create a file. See also: priority.

source
PkgTemplates.hookFunction.
hook(::Plugin, ::Template, pkg_dir::AbstractString)

Stage 2 of the package generation pipeline (the "main" stage, in general). At this point, the prehooks have run, but not the posthooks.

pkg_dir is the directory in which the package is being generated (so basename(pkg_dir) is the package name).

Note

You usually shouldn't implement this function for BasicPlugins. If you do, it should probably invoke the generic method (otherwise, there's not much reason to subtype BasicPlugin).

source
PkgTemplates.posthookFunction.
posthook(::Plugin, ::Template, pkg_dir::AbstractString)

Stage 3 of the package generation pipeline (the "after" stage, in general). At this point, both the prehooks and hooks have run.

source
PkgTemplates.priorityFunction.
priority(::Plugin, ::Union{typeof(prehook), typeof(hook), typeof(posthook)}) -> Int

Determines the order in which plugins are processed (higher goes first). The default priority (DEFAULT_PRIORITY), is 1000.

You can implement this function per-stage (by using ::typeof(hook), for example), or for all stages by simply using ::Function.

source

Plugin Walkthrough

Concrete types that subtype Plugin directly are free to do almost anything. To understand how they're implemented, let's look at simplified versions of two plugins: Documenter to explore templating, and Git to further clarify the multi-stage pipeline.

Example: Documenter

@with_kw_noshow struct Documenter <: Plugin
+    make_jl::String = default_file("docs", "make.jl") <- "Path to make.jl template"
+    index_md::String = default_file("docs", "src", "index.md") <- "Path to index.md template"
+end
+
+gitignore(::Documenter) = ["/docs/build/"]
+
+badges(::Documenter) = [
+    Badge(
+        "Stable",
+        "https://img.shields.io/badge/docs-stable-blue.svg",
+        "https://{{{USER}}}.github.io/{{{PKG}}}.jl/stable",
+    ),
+    Badge(
+        "Dev",
+        "https://img.shields.io/badge/docs-dev-blue.svg",
+        "https://{{{USER}}}.github.io/{{{PKG}}}.jl/dev",
+    ),
+]
+
+view(p::Documenter, t::Template, pkg::AbstractString) = Dict(
+    "AUTHORS" => join(t.authors, ", "),
+    "PKG" => pkg,
+    "REPO" => "$(t.host)/$(t.user)/$pkg.jl",
+    "USER" => t.user,
+)
+
+function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
+    pkg = basename(pkg_dir)
+    docs_dir = joinpath(pkg_dir, "docs")
+
+    make = render_file(p.make_jl, combined_view(p, t, pkg), tags(p))
+    gen_file(joinpath(docs_dir, "make.jl"), make)
+    
+    index = render_file(p.index_md, combined_view(p, t, pkg), tags(p))
+    gen_file(joinpath(docs_dir, "src", "index.md"), index)
+
+    # What this function does is not relevant here.
+    create_documentation_project()
+end

The @with_kw_noshow macro defines keyword constructors for us. Inside of our struct definition, we're using default_file to refer to files in this repository.

default_file(paths::AbstractString...) -> String

Return a path relative to the default template file directory (~/build/invenia/PkgTemplates.jl/templates).

source

The first method we implement for Documenter is gitignore, so that packages created with this plugin ignore documentation build artifacts.

gitignore(::Plugin) -> Vector{String}

Return patterns that should be added to .gitignore. These are used by the Git plugin.

By default, an empty list is returned.

source

Second, we implement badges to add a couple of badges to new packages' README files.

PkgTemplates.badgesFunction.
badges(::Plugin) -> Union{Badge, Vector{Badge}}

Return a list of Badges, or just one, to be added to README.md. These are used by the Readme plugin to add badges to the README.

By default, an empty list is returned.

source
Badge(hover::AbstractString, image::AbstractString, link::AbstractString)

Container for Markdown badge data. Each argument can contain placeholders, which will be filled in with values from combined_view.

Arguments

  • hover::AbstractString: Text to appear when the mouse is hovered over the badge.
  • image::AbstractString: URL to the image to display.
  • link::AbstractString: URL to go to upon clicking the badge.
source

These two functions, gitignore and badges, are currently the only "special" functions for cross-plugin interactions. In other cases, you can still access the Template's plugins to depend on the presence/properties of other plugins, although that's less powerful.

Third, we implement view, which is used to fill placeholders in badges and rendered files.

PkgTemplates.viewFunction.
view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}

Return the view to be passed to the text templating engine for this plugin. pkg is the name of the package being generated.

For BasicPlugins, this is used for both the plugin badges (see badges) and the template file (see source). For other Plugins, it is used only for badges, but you can always call it yourself as part of your hook implementation.

By default, an empty Dict is returned.

source

Finally, we implement hook, which is the real workhorse for the plugin. Inside of this function, we generate a couple of files with the help of a few more text templating functions.

render_file(file::AbstractString view::Dict{<:AbstractString}, tags) -> String

Render a template file with the data in view. tags should be a tuple of two strings, which are the opening and closing delimiters, or nothing to use the default delimiters.

source
render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=nothing) -> String

Render some text with the data in view. tags should be a tuple of two strings, which are the opening and closing delimiters, or nothing to use the default delimiters.

source
PkgTemplates.gen_fileFunction.
gen_file(file::AbstractString, text::AbstractString)

Create a new file containing some given text. Trailing whitespace is removed, and the file will end with a newline.

source
combined_view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}

This function combines view and user_view for use in text templating. If you're doing manual file creation or text templating (i.e. writing Plugins that are not BasicPlugins), then you should use this function rather than either of the former two.

Note

Do not implement this function yourself! If you're implementing a plugin, you should implement view. If you're customizing a plugin as a user, you should implement user_view.

source
PkgTemplates.tagsFunction.
tags(::Plugin) -> Tuple{String, String}

Return the delimiters used for text templating. See the Citation plugin for a rare case where changing the tags is necessary.

By default, the tags are "{{" and "}}".

source

For more information on text templating, see the BasicPlugin Walkthrough and the section on Custom Template Files.

Example: Git

struct Git <: Plugin end
+
+priority(::Git, ::typeof(posthook)) = 5
+
+function validate(::Git, ::Template)
+    foreach(("user.name", "user.email")) do k
+        if isempty(LibGit2.getconfig(k, ""))
+            throw(ArgumentError("Git: Global Git config is missing required value '$k'"))
+        end
+    end
+end
+
+function prehook(::Git, t::Template, pkg_dir::AbstractString)
+    LibGit2.with(LibGit2.init(pkg_dir)) do repo
+        LibGit2.commit(repo, "Initial commit")
+        pkg = basename(pkg_dir)
+        url = "https://$(t.host)/$(t.user)/$pkg.jl"
+        close(GitRemote(repo, "origin", url))
+    end
+end
+
+function hook(::Git, t::Template, pkg_dir::AbstractString)
+    ignore = mapreduce(gitignore, append!, t.plugins)
+    unique!(sort!(ignore))
+    gen_file(joinpath(pkg_dir, ".gitignore"), join(ignore, "\n"))
+end
+
+function posthook(::Git, ::Template, pkg_dir::AbstractString)
+    LibGit2.with(GitRepo(pkg_dir)) do repo
+        LibGit2.add!(repo, ".")
+        LibGit2.commit(repo, "Files generated by PkgTemplates")
+    end
+end

Validation and all three hooks are implemented:

As previously mentioned, we use priority to make sure that we wait until all other plugins are finished their work before committing files.

Hopefully, this demonstrates the level of control you have over the package generation process when developing plugins, and when it makes sense to exercise that power!

BasicPlugin Walkthrough

Most of the time, you don't really need all of the control that we showed off above. Plugins that subtype BasicPlugin perform a much more limited task. In general, they just generate one templated file.

To illustrate, let's look at the Citation plugin, which creates a CITATION.bib file.

@with_kw_noshow struct Citation <: BasicPlugin
+    file::String = default_file("CITATION.bib")
+end
+
+source(p::Citation) = p.file
+destination(::Citation) = "CITATION.bib"
+
+tags(::Citation) = "<<", ">>"
+
+view(::Citation, t::Template, pkg::AbstractString) = Dict(
+    "AUTHORS" => join(t.authors, ", "),
+    "MONTH" => month(today()),
+    "PKG" => pkg,
+    "URL" => "https://$(t.host)/$(t.user)/$pkg.jl",
+    "YEAR" => year(today()),
+)

Similar to the Documenter example above, we're defining a keyword constructor, and assigning a default template file from this repository. This plugin adds nothing to .gitignore, and it doesn't add any badges, so implementations for gitignore and badges are omitted.

First, we implement source and destination to define where the template file comes from, and where it goes. These functions are specific to BasicPlugins, and have no effect on regular Plugins by default.

PkgTemplates.sourceFunction.
source(::BasicPlugin) -> Union{String, Nothing}

Return the path to a plugin's template file, or nothing to indicate no file.

By default, nothing is returned.

source
destination(::BasicPlugin) -> String

Return the destination, relative to the package root, of a plugin's configuration file.

This function must be implemented.

source

Next, we implement tags. We briefly saw this function earlier, but in this case it's necessary to change its behaviour from the default. To see why, it might help to see the template file in its entirety:

@misc{<<&PKG>>.jl,
+	author  = {<<&AUTHORS>>},
+	title   = {<<&PKG>>.jl},
+	url     = {<<&URL>>},
+	version = {v0.1.0},
+	year    = {<<&YEAR>>},
+	month   = {<<&MONTH>>}
+}

Because the file contains its own {} delimiters, we need to use different ones for templating to work properly.

Finally, we implement view to fill in the placeholders that we saw in the template file.

Doing Extra Work With BasicPlugins

Notice that we didn't have to implement hook for our plugin. It's implemented for all BasicPlugins, like so:

function render_plugin(p::BasicPlugin, t::Template, pkg::AbstractString)
+    return render_file(source(p), combined_view(p, t, pkg), tags(p))
+end
+
+function hook(p::BasicPlugin, t::Template, pkg_dir::AbstractString)
+    source(p) === nothing && return
+    pkg = basename(pkg_dir)
+    path = joinpath(pkg_dir, destination(p))
+    text = render_plugin(p, t, pkg)
+    gen_file(path, text)
+end

But what if we want to do a little more than just generate one file?

A good example of this is the Tests plugin. It creates runtests.jl, but it also modifies the Project.toml to include the Test dependency.

Of course, we could use a normal Plugin, but it turns out there's a way to avoid that while still getting the extra capbilities that we want.

The plugin implements its own hook, but uses invoke to avoid duplicating the file creation code:

@with_kw_noshow struct Tests <: BasicPlugin
+    file::String = default_file("runtests.jl")
+end
+
+source(p::Tests) = p.file
+destination(::Tests) = joinpath("test", "runtests.jl")
+view(::Tests, ::Template, pkg::AbstractString) = Dict("PKG" => pkg)
+
+function hook(p::Tests, t::Template, pkg_dir::AbstractString)
+    # Do the normal BasicPlugin behaviour to create the test script.
+    invoke(hook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)
+    # Do some other work.
+    add_test_dependency()
+end

There is also a default validate implementation for BasicPlugins, which checks that the plugin's source file exists, and throws an ArgumentError otherwise. If you want to extend the validation but keep the file existence check, use the invoke method as described above.

For more examples, see the plugins in the Continuous Integration (CI) and Code Coverage sections.

Miscellaneous Tips

Writing Template Files

For an overview of writing template files for Mustache.jl, see Custom Template Files in the user guide.

Predicates

There are a few predicate functions for plugins that are occasionally used to answer questions like "does this Template have any code coverage plugins?". If you're implementing a plugin that fits into one of the following categories, it would be wise to implement the corresponding predicate function to return true for instances of your type.

needs_username(::Plugin) -> Bool

Determine whether or not a plugin needs a Git hosting service username to function correctly. If you are implementing a plugin that uses the user field of a Template, you should implement this function and return true.

source
PkgTemplates.is_ciFunction.
is_ci(::Plugin) -> Bool

Determine whether or not a plugin is a CI plugin. If you are adding a CI plugin, you should implement this function and return true.

source
is_coverage(::Plugin) -> Bool

Determine whether or not a plugin is a coverage plugin. If you are adding a coverage plugin, you should implement this function and return true.

source

Formatting Version Numbers

When writing configuration files for CI services, working with version numbers is often needed. There are a few convenience functions that can be used to make this a little bit easier.

compat_version(v::VersionNumber) -> String

Format a VersionNumber to exclude trailing zero components.

source
format_version(v::Union{VersionNumber, AbstractString}) -> String

Strip everything but the major and minor release from a VersionNumber. Strings are left in their original form.

source
collect_versions(t::Template, versions::Vector) -> Vector{String}

Combine t's Julia version with versions, and format them as major.minor. This is useful for creating lists of versions to be included in CI configurations.

source
diff --git a/dev/index.html b/dev/index.html index cfbb434..66afd71 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,204 +1,2 @@ -Home · PkgTemplates.jl

Home

PkgTemplates

Stable Latest Build Status Build Status Codecov

PkgTemplates is a Julia package for creating new Julia packages in an easy, repeatable, and customizable way.

Installation

pkg> add PkgTemplates

Plugins

PkgTemplates is based on plugins which handle the setup of individual package components. The available plugins are:

Usage

Assuming you have the relatively standard Git options user.name, user.email and github.user set up globally with git config --global, the simplest template requires no arguments:

julia> using PkgTemplates
-
-julia> t = Template()
-Template:
-  → User: travis
-  → Host: github.com
-  → License: MIT (Travis CI User <travis@example.org> 2019)
-  → Package directory: ~/.julia/dev
-  → Minimum Julia version: v1.0
-  → SSH remote: No
-  → Add packages to main environment: Yes
-  → Commit Manifest.toml: No
-  → Plugins: None
-
-julia> generate("MyPkg", t)
-Generating project MyPkg:
-    /home/travis/.julia/dev/MyPkg/Project.toml
-    /home/travis/.julia/dev/MyPkg/src/MyPkg.jl
-[ Info: Initialized Git repo at /home/travis/.julia/dev/MyPkg
-[ Info: Set remote origin to https://github.com/travis/MyPkg.jl
- Resolving package versions...
-  Updating `~/.julia/dev/MyPkg/Project.toml`
-  [8dfed614] + Test 
-  Updating `~/.julia/dev/MyPkg/Manifest.toml`
-  [2a0f44e3] + Base64 
-  [8ba89e20] + Distributed 
-  [b77e0a4c] + InteractiveUtils 
-  [56ddb016] + Logging 
-  [d6f4376e] + Markdown 
-  [9a3f8284] + Random 
-  [9e88b42a] + Serialization 
-  [6462fe0b] + Sockets 
-  [8dfed614] + Test 
-  Updating registry at `~/.julia/registries/General`
-  Updating git-repo `https://github.com/JuliaRegistries/General.git`
-[?25l[?25h Resolving package versions...
-  Updating `~/.julia/dev/MyPkg/Project.toml`
- [no changes]
-  Updating `~/.julia/dev/MyPkg/Manifest.toml`
-  [2a0f44e3] - Base64 
-  [8ba89e20] - Distributed 
-  [b77e0a4c] - InteractiveUtils 
-  [56ddb016] - Logging 
-  [d6f4376e] - Markdown 
-  [9a3f8284] - Random 
-  [9e88b42a] - Serialization 
-  [6462fe0b] - Sockets 
-  [8dfed614] - Test 
-[ Info: activating new environment at ~/build/invenia/PkgTemplates.jl/docs/Project.toml.
-[ Info: Committed 6 files/directories: src/, Project.toml, test/, README.md, LICENSE, .gitignore
- Resolving package versions...
-  Updating `~/build/invenia/PkgTemplates.jl/docs/Project.toml`
-  [8557d362] + MyPkg v0.1.0 [`~/.julia/dev/MyPkg`]
-  Updating `~/build/invenia/PkgTemplates.jl/docs/Manifest.toml`
-  [8557d362] + MyPkg v0.1.0 [`~/.julia/dev/MyPkg`]
-[ Info: New package is at /home/travis/.julia/dev/MyPkg
-
-julia> run(`git -C $(joinpath(t.dir, "MyPkg")) ls-files`);
-.gitignore
-LICENSE
-Project.toml
-README.md
-src/MyPkg.jl
-test/runtests.jl

However, we can also configure a number of keyword arguments to Template:

julia> using PkgTemplates
-
-julia> t = Template(;
-           user="myusername",
-           license="MIT",
-           authors=["Chris de Graaf", "Invenia Technical Computing Corporation"],
-           dir="~/code",
-           julia_version=v"0.7",
-           ssh=true,
-           plugins=[
-               TravisCI(),
-               Codecov(),
-               Coveralls(),
-               AppVeyor(),
-               GitHubPages(),
-               CirrusCI(),
-           ],
-       )
-Template:
-  → User: myusername
-  → Host: github.com
-  → License: MIT (Chris de Graaf, Invenia Technical Computing Corporation 2019)
-  → Package directory: ~/code
-  → Minimum Julia version: v0.7
-  → SSH remote: Yes
-  → Add packages to main environment: Yes
-  → Commit Manifest.toml: No
-  → Plugins:
-    • AppVeyor:
-      → Config file: Default
-      → 0 gitignore entries
-    • CirrusCI:
-      → 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("MyPkg2", t)
-Generating project MyPkg2:
-    /home/travis/code/MyPkg2/Project.toml
-    /home/travis/code/MyPkg2/src/MyPkg2.jl
-[ Info: Initialized Git repo at /home/travis/code/MyPkg2
-[ Info: Set remote origin to git@github.com:myusername/MyPkg2.jl.git
-[ Info: Created empty gh-pages branch
- Resolving package versions...
-[ Warning: julia version requirement for project not satisfied
-  Updating `~/code/MyPkg2/Project.toml`
-  [8dfed614] + Test 
-  Updating `~/code/MyPkg2/Manifest.toml`
-  [2a0f44e3] + Base64 
-  [8ba89e20] + Distributed 
-  [b77e0a4c] + InteractiveUtils 
-  [56ddb016] + Logging 
-  [d6f4376e] + Markdown 
-  [9a3f8284] + Random 
-  [9e88b42a] + Serialization 
-  [6462fe0b] + Sockets 
-  [8dfed614] + Test 
-  Updating registry at `~/.julia/registries/General`
-  Updating git-repo `https://github.com/JuliaRegistries/General.git`
-[?25l[?25h Resolving package versions...
-[ Warning: julia version requirement for project not satisfied
-  Updating `~/code/MyPkg2/Project.toml`
- [no changes]
-  Updating `~/code/MyPkg2/Manifest.toml`
-  [2a0f44e3] - Base64 
-  [8ba89e20] - Distributed 
-  [b77e0a4c] - InteractiveUtils 
-  [56ddb016] - Logging 
-  [d6f4376e] - Markdown 
-  [9a3f8284] - Random 
-  [9e88b42a] - Serialization 
-  [6462fe0b] - Sockets 
-  [8dfed614] - Test 
-[ Info: activating new environment at ~/build/invenia/PkgTemplates.jl/docs/Project.toml.
- Resolving package versions...
- Installed Parsers ──── v0.3.7
- Installed JSON ─────── v0.21.0
- Installed Documenter ─ v0.23.3
-  Updating `~/code/MyPkg2/docs/Project.toml`
-  [e30172f5] + Documenter v0.23.3
-  Updating `~/code/MyPkg2/docs/Manifest.toml`
-  [ffbed154] + DocStringExtensions v0.8.0
-  [e30172f5] + Documenter v0.23.3
-  [682c06a0] + JSON v0.21.0
-  [69de0a69] + Parsers v0.3.7
-  [2a0f44e3] + Base64 
-  [ade2ca70] + Dates 
-  [8ba89e20] + Distributed 
-  [b77e0a4c] + InteractiveUtils 
-  [76f85450] + LibGit2 
-  [56ddb016] + Logging 
-  [d6f4376e] + Markdown 
-  [a63ad114] + Mmap 
-  [44cfe95a] + Pkg 
-  [de0858da] + Printf 
-  [3fa0cd96] + REPL 
-  [9a3f8284] + Random 
-  [ea8e919c] + SHA 
-  [9e88b42a] + Serialization 
-  [6462fe0b] + Sockets 
-  [8dfed614] + Test 
-  [cf7118a7] + UUIDs 
-  [4ec0a83e] + Unicode 
-[ Info: activating new environment at ~/build/invenia/PkgTemplates.jl/docs/Project.toml.
-[ Info: Committed 10 files/directories: src/, Project.toml, test/, README.md, LICENSE, docs/, .cirrus.yml, .appveyor.yml, .travis.yml, .gitignore
-[ Info: Remember to push all created branches to your remote: git push --all
- Resolving package versions...
-┌ Warning: julia version requirement for package MyPkg2 not satisfied
-└ @ Pkg.Operations /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Pkg/src/Operations.jl:154
-  Updating `~/build/invenia/PkgTemplates.jl/docs/Project.toml`
-  [886399ce] + MyPkg2 v0.1.0 [`~/code/MyPkg2`]
-  Updating `~/build/invenia/PkgTemplates.jl/docs/Manifest.toml`
-  [886399ce] + MyPkg2 v0.1.0 [`~/code/MyPkg2`]
-[ Info: New package is at /home/travis/code/MyPkg2
-
-julia> run(`git -C $(joinpath(t.dir, "MyPkg2")) ls-files`);
-.appveyor.yml
-.cirrus.yml
-.gitignore
-.travis.yml
-LICENSE
-Project.toml
-README.md
-docs/Manifest.toml
-docs/Project.toml
-docs/make.jl
-docs/src/index.md
-src/MyPkg2.jl
-test/runtests.jl

If that looks like a lot of work, you can also create templates interactively with interactive_template:

asciicast

And if that's still too much work for you, you can call interactive_template with fast=true to use default values for everything but username and plugin selection.

You can also use generate_interactive to interactively generate a template and then immediately use it to create a new package.

Comparison to PkgDev

PkgTemplates is similar in functionality to PkgDev's generate function. However, PkgTemplates offers more customizability in templates and more extensibility via plugins. For the package registration and release management features that PkgTemplates doesn't include, you are encouraged to use AttoBot instead.

Contributing

It's extremely easy to extend PkgTemplates with new plugins. To get started, check out the plugin development guide.

+Home · PkgTemplates.jl

Home

PkgTemplates

PkgTemplates creates new Julia packages in an easy, repeatable, and customizable way.

Documentation

If you're looking to create new packages, see the User Guide.

If you want to create new plugins, see the Developer Guide.

if you're trying to migrate from an older version of PkgTemplates, see Migrating To PkgTemplates 0.7+.

Index

diff --git a/dev/migrating/index.html b/dev/migrating/index.html new file mode 100644 index 0000000..1b48475 --- /dev/null +++ b/dev/migrating/index.html @@ -0,0 +1,2 @@ + +Migrating To PkgTemplates 0.7+ · PkgTemplates.jl

Migrating To PkgTemplates 0.7+

Migrating To PkgTemplates 0.7+

PkgTemplates 0.7 is a ground-up rewrite of the package with similar functionality but with updated APIs and internals. Here is a summary of things that existed in older versions but have been moved elsewhere or removed. However, it might be easier to just read the User Guide.

Template keywords

The recurring theme is "everything is a plugin now".

OldNew
license="ISC"plugins=[License(; name="ISC")]
develop=true *plugins=[Develop()]
git=falsedisable_defaults=[Git]
julia_version=v"1"julia=v"1"
ssh=trueplugins=[Git(; ssh=true)]
manifest=trueplugins=[Git(; manifest=true)]

* develop=true was the default setting, but it is no longer the default in PkgTemplates 0.7+.

Plugins

Aside from renamings, basically every plugin has had their constructors reworked. So if you are using anything non-default, you should consult the new docstring.

OldNew
GitHubPagesDocumenter{TravisCI}
GitLabPagesDocumenter{GitLabCI}

Package Generation

One less name to remember!

OldNew
generate(::Template, pkg::AbstractString)(::Template)(pkg::AbstractString)

Interactive Templates

Currently not implemented, but will be in the future.

Other Functions

Two less names to remember! Although it's unlikely that anyone used these.

OldNew
available_licensesView licenses on GitHub
show_licenseView licenses on GitHub

Custom Plugins

In addition to the changes in usage, custom plugins from older versions of PkgTemplates will not work in 0.7+. See the Developer Guide for more information on the new extension API.

diff --git a/dev/pages/index.html b/dev/pages/index.html deleted file mode 100644 index 8abc0b4..0000000 --- a/dev/pages/index.html +++ /dev/null @@ -1,2 +0,0 @@ - -Index · PkgTemplates.jl

Index

Index

diff --git a/dev/pages/licenses/index.html b/dev/pages/licenses/index.html deleted file mode 100644 index f5110e2..0000000 --- a/dev/pages/licenses/index.html +++ /dev/null @@ -1,2 +0,0 @@ - -Licenses · PkgTemplates.jl

Licenses

Licenses

Many open-source licenses are available for use with PkgTemplates, but if you see that one is missing, don't hesitate to open an issue or PR.

available_licenses([io::IO]) -> Nothing

Print the names of all available licenses.

source
show_license([io::IO], license::AbstractString) -> Nothing

Print the text of license. Errors if the license is not found.

source

Helper Functions

read_license(license::AbstractString) -> String

Returns the contents of license. Errors if the license is not found. Use available_licenses to view available licenses.

source
diff --git a/dev/pages/package_generation/index.html b/dev/pages/package_generation/index.html deleted file mode 100644 index ee62a22..0000000 --- a/dev/pages/package_generation/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -Package Generation · PkgTemplates.jl

Package Generation

Package Generation

Creating new packages with PkgTemplates revolves around creating a new Template, then calling generate on it.

Template

Template(; kwargs...) -> Template

Records common information used to generate a package. If you don't wish to manually create a template, you can use interactive_template instead.

Keyword Arguments

  • user::AbstractString="": GitHub (or other code hosting service) username. If left unset, it will take the the global git config's value (github.user). If that is not set, an ArgumentError is thrown. This is case-sensitive for some plugins, so take care to enter it correctly.
  • host::AbstractString="github.com": URL to the code hosting service where your package will reside. Note that while hosts other than GitHub won't cause errors, they are not officially supported and they will cause certain plugins will produce incorrect output.
  • license::AbstractString="MIT": Name of the package license. If an empty string is given, no license is created. available_licenses can be used to list all available licenses, and show_license can be used to print out a particular license's text.
  • authors::Union{AbstractString, Vector{<:AbstractString}}="": Names that appear on the license. Supply a string for one author or an array for multiple. Similarly to user, it will take the value of of the global git config's value if it is left unset.
  • dir::AbstractString=~/.julia/dev: Directory in which the package will go. Relative paths are converted to absolute ones at template creation time.
  • julia_version::VersionNumber=1.0.0: Minimum allowed Julia version.
  • ssh::Bool=false: Whether or not to use SSH for the git remote. If false HTTPS will be used.
  • dev::Bool=true: Whether or not to Pkg.develop generated packages.
  • manifest::Bool=false: Whether or not to commit the Manifest.toml.
  • plugins::Vector{<:Plugin}=Plugin[]: A list of Plugins that the package will include.
source
interactive_template(; fast::Bool=false) -> Template

Interactively create a Template. If fast is set, defaults will be assumed for all values except username and plugins.

source

generate

PkgTemplates.generateFunction.
generate(pkg::AbstractString, t::Template) -> Nothing
-generate(t::Template, pkg::AbstractString) -> Nothing

Generate a package named pkg from t. If git is false, no Git repository is created.

source
generate_interactive(pkg::AbstractString; fast::Bool=false, git::Bool=true) -> Template

Interactively create a template, and then generate a package with it. Arguments and keywords are used in the same way as in generate and interactive_template.

source

Helper Functions

gen_tests(pkg_dir::AbstractString, t::Template) -> Vector{String}

Create the test entrypoint in pkg_dir.

Arguments

  • pkg_dir::AbstractString: The package directory in which the files will be generated
  • t::Template: The template whose tests we are generating.

Returns an array of generated file/directory names.

source
gen_readme(pkg_dir::AbstractString, t::Template) -> Vector{String}

Create a README in pkg_dir with badges for each enabled plugin.

Arguments

  • pkg_dir::AbstractString: The directory in which the files will be generated.
  • t::Template: The template whose README we are generating.

Returns an array of generated file/directory names.

source
gen_gitignore(pkg_dir::AbstractString, t::Template) -> Vector{String}

Create a .gitignore in pkg_dir.

Arguments

  • pkg_dir::AbstractString: The directory in which the files will be generated.
  • t::Template: The template whose .gitignore we are generating.

Returns an array of generated file/directory names.

source
gen_license(pkg_dir::AbstractString, t::Template) -> Vector{String}

Create a license in pkg_dir.

Arguments

  • pkg_dir::AbstractString: The directory in which the files will be generated.
  • t::Template: The template whose LICENSE we are generating.

Returns an array of generated file/directory names.

source
diff --git a/dev/pages/plugin_development/index.html b/dev/pages/plugin_development/index.html deleted file mode 100644 index f673583..0000000 --- a/dev/pages/plugin_development/index.html +++ /dev/null @@ -1,74 +0,0 @@ - -Plugin Development · PkgTemplates.jl

Plugin Development

Plugin Development

The best and easiest way to contribute to PkgTemplates is to write new plugins.

A plugin to be added to a Template, which adds some functionality or integration. New plugins should almost always extend GenericPlugin or CustomPlugin.

source

Generic Plugins

Generic plugins are plugins that add any number of patterns to the generated package's .gitignore, and have at most one associated file to generate.

Attributes

  • gitignore::Vector{AbstractString}: Array of patterns to be added to the .gitignore of generated packages that use this plugin.
  • src::Union{AbstractString, Nothing}: Path to the file that will be copied into the generated package repository. If set to nothing, no file will be generated. When this defaults to an empty string, there should be a default file in defaults that will be copied. That file's name is usually the same as the plugin's name, except in all lowercase and with the .yml extension. If this is not the case, an interactive method needs to be implemented to call interactive(; file="file.ext").
  • dest::AbstractString: Path to the generated file, relative to the root of the generated package repository.
  • badges::Vector{Badge}: Array of Badges containing information used to create Markdown-formatted badges from the plugin. Entries will be run through substitute, so they may contain placeholder values.
  • view::Dict{String, Any}: Additional substitutions to make in both the plugin's badges and its associated file. See substitute for details.

Example

struct MyPlugin <: GenericPlugin
-    gitignore::Vector{AbstractString}
-    src::Union{AbstractString, Nothing}
-    dest::AbstractString
-    badges::Vector{Badge}
-    view::Dict{String, Any}
-
-    function MyPlugin(; config_file::Union{AbstractString, Nothing}="")
-        if config_file != nothing
-            config_file = if isempty(config_file)
-                joinpath(DEFAULTS_DIR, "my-plugin.toml")
-            elseif isfile(config_file)
-                abspath(config_file)
-            else
-                throw(ArgumentError(
-                    "File $(abspath(config_file)) does not exist"
-                ))
-            end
-        end
-        new(
-            ["*.mgp"],
-            config_file,
-            ".my-plugin.toml",
-            [
-                Badge(
-                    "My Plugin",
-                    "https://myplugin.com/badge-{{YEAR}}.png",
-                    "https://myplugin.com/{{USER}}/{{PKGNAME}}.jl",
-                ),
-            ],
-            Dict{String, Any}("YEAR" => year(today())),
-        )
-    end
-end
-
-interactive(::Type{MyPlugin}) = interactive(MyPlugin; file="my-plugin.toml")

The above plugin ignores files ending with .mgp, copies defaults/my-plugin.toml by default, and creates a badge that links to the project on its own site, using the default substitutions with one addition: {{YEAR}} => year(today()). Since the default config template file doesn't follow the generic naming convention, we added another interactive method to correct the assumed filename.

source

Custom Plugins

Custom plugins are plugins whose behaviour does not follow the GenericPlugin pattern. They can implement gen_plugin, badges, and interactive in any way they choose, as long as they conform to the usual type signature.

Attributes

  • gitignore::Vector{AbstractString}: Array of patterns to be added to the .gitignore of generated packages that use this plugin.

Example

struct MyPlugin <: CustomPlugin
-    gitignore::Vector{AbstractString}
-    lucky::Bool
-
-    MyPlugin() = new([], rand() > 0.8)
-
-    function gen_plugin(p::MyPlugin, t::Template, pkg_name::AbstractString)
-        return if p.lucky
-            text = substitute("You got lucky with {{PKGNAME}}, {{USER}}!", t)
-            gen_file(joinpath(t.dir, pkg_name, ".myplugin.yml"), text)
-            [".myplugin.yml"]
-        else
-            println("Maybe next time.")
-            String[]
-        end
-    end
-
-    function badges(p::MyPlugin, user::AbstractString, pkg_name::AbstractString)
-        return if p.lucky
-            [
-                format(Badge(
-                    "You got lucky!",
-                    "https://myplugin.com/badge.png",
-                    "https://myplugin.com/$user/$pkg_name.jl",
-                )),
-            ]
-        else
-            String[]
-        end
-    end
-end
-
-interactive(:Type{MyPlugin}) = MyPlugin()

This plugin doesn't do much, but it demonstrates how gen_plugin, badges and interactive can be implemented using substitute, gen_file, Badge, and format.

Defining Template Files

Often, the contents of the config file that your plugin generates depends on variables like the package name, the user's username, etc. Template files (which are stored in defaults) can use here's syntax to define replacements.

source

CustomPlugin Required Methods

gen_plugin

gen_plugin(p::Plugin, t::Template, pkg_name::AbstractString) -> Vector{String}

Generate any files associated with a plugin.

Arguments

  • p::Plugin: Plugin whose files are being generated.
  • t::Template: Template configuration.
  • pkg_name::AbstractString: Name of the package.

Returns an array of generated file/directory names.

source
interactive(T::Type{<:Plugin}; file::Union{AbstractString, Nothing}="") -> Plugin

Interactively create a plugin of type T, where file is the plugin type's default config template with a non-standard name (for MyPlugin, this is anything but "myplugin.yml").

source

Note: interactive is not strictly required, however without it, your custom plugin will not be available when creating templates with interactive_template.

badges

PkgTemplates.badgesFunction.
badges(p::Plugin, user::AbstractString, pkg_name::AbstractString) -> Vector{String}

Generate Markdown badges for the plugin.

Arguments

  • p::Plugin: Plugin whose badges we are generating.
  • user::AbstractString: Username of the package creator.
  • pkg_name::AbstractString: Name of the package.

Returns an array of Markdown badges.

source

Helper Types/Functions

gen_file

PkgTemplates.gen_fileFunction.
gen_file(file::AbstractString, text::AbstractString) -> Int

Create a new file containing some given text. Always ends the file with a newline.

Arguments

  • file::AbstractString: Path to the file to be created.
  • text::AbstractString: Text to write to the file.

Returns the number of bytes written to the file.

source

substitute

substitute(template::AbstractString, view::Dict{String, Any}) -> String
-substitute(
-    template::AbstractString,
-    pkg_template::Template;
-    view::Dict{String, Any}=Dict{String, Any}(),
-) -> String

Replace placeholders in template with values in view via Mustache. template is not modified. If pkg_template is supplied, some default replacements are also performed.

For information on how to structure template, see "Defining Template Files" section in Custom Plugins.

Note: Conditionals in template without a corresponding key in view won't error, but will simply be evaluated as false.

source

Badge

Badge(hover::AbstractString, image::AbstractString, link::AbstractString) -> Badge

A Badge contains the data necessary to generate a Markdown badge.

Arguments

  • hover::AbstractString: Text to appear when the mouse is hovered over the badge.
  • image::AbstractString: URL to the image to display.
  • link::AbstractString: URL to go to upon clicking the badge.
source

format

PkgTemplates.formatFunction.
format(b::Badge) -> String

Return badge's data formatted as a Markdown string.

source

version_floor

version_floor(v::VersionNumber=VERSION) -> String

Format the given Julia version.

Keyword arguments

  • v::VersionNumber=VERSION: Version to floor.

Returns "major.minor" for the most recent release version relative to v. For prereleases with v.minor == v.patch == 0, returns "major.minor-".

source
diff --git a/dev/pages/plugins/index.html b/dev/pages/plugins/index.html deleted file mode 100644 index 664d660..0000000 --- a/dev/pages/plugins/index.html +++ /dev/null @@ -1,2 +0,0 @@ - -Plugins · PkgTemplates.jl

Plugins

Plugins

Plugins are the secret sauce behind PkgTemplates's customization and extension. This page describes plugins that already exist; for information on writing your own plugins, see Plugin Development.

Continuous Integration (CI)

TravisCI(; config_file::Union{AbstractString, Nothing}="") -> TravisCI

Add TravisCI to a template's plugins to add a .travis.yml configuration file to generated repositories, and an appropriate badge to the README.

Keyword Arguments:

  • config_file::Union{AbstractString, Nothing}="": Path to a custom .travis.yml. If nothing is supplied, no file will be generated.
source
AppVeyor(; config_file::Union{AbstractString, Nothing}="") -> AppVeyor

Add AppVeyor to a template's plugins to add a .appveyor.yml configuration file to generated repositories, and an appropriate badge to the README.

Keyword Arguments

  • config_file::Union{AbstractString, Nothing}="": Path to a custom .appveyor.yml. If nothing is supplied, no file will be generated.
source
GitLabCI(; config_file::Union{AbstractString, Nothing}="", coverage::Bool=true) -> GitLabCI

Add GitLabCI to a template's plugins to add a .gitlab-ci.yml configuration file to generated repositories, and appropriate badge(s) to the README.

Keyword Arguments:

  • config_file::Union{AbstractString, Nothing}="": Path to a custom .gitlab-ci.yml. If nothing is supplied, no file will be generated.
  • coverage::Bool=true: Whether or not GitLab CI's built-in code coverage analysis should be enabled.
source
CirrusCI(; config_file::Union{AbstractString, Nothing}="") -> CirrusCI

Add CirrusCI to a template's plugins to add a .cirrus.yml configuration file to generated repositories, and an appropriate badge to the README. The default configuration file supports only FreeBSD builds via CirrusCI.jl

Keyword Arguments

  • config_file::Union{AbstractString, Nothing}="": Path to a custom .cirrus.yml. If nothing is supplied, no file will be generated.
source

Code Coverage

Codecov(; config_file::Union{AbstractString, Nothing}=nothing) -> Codecov

Add Codecov to a template's plugins to optionally add a .codecov.yml configuration file to generated repositories, and an appropriate badge to the README. Also updates the .gitignore accordingly.

Keyword Arguments:

  • config_file::Union{AbstractString, Nothing}=nothing: Path to a custom .codecov.yml. If left unset, no file will be generated.
source
Coveralls(; config_file::Union{AbstractString, Nothing}=nothing) -> Coveralls

Add Coveralls to a template's plugins to optionally add a .coveralls.yml configuration file to generated repositories, and an appropriate badge to the README. Also updates the .gitignore accordingly.

Keyword Arguments:

  • config_file::Union{AbstractString, Nothing}=nothing: Path to a custom .coveralls.yml. If left unset, no file will be generated.
source

Documentation

Add a Documenter subtype to a template's plugins to add support for documentation generation via Documenter.jl.

By default, the plugin generates a minimal index.md and a make.jl file. The make.jl file contains the Documenter.makedocs command with predefined values for modules, format, pages, repo, sitename, and authors.

The subtype is expected to include the following fields:

  • assets::Vector{AbstractString}, a list of filenames to be included as the assets

kwarg to makedocs

  • gitignore::Vector{AbstractString}, a list of files to be added to the .gitignore

It may optionally include the field additional_kwargs::Union{AbstractDict, NamedTuple} to allow additional kwargs to be added to makedocs.

source
GitHubPages(; assets::Vector{<:AbstractString}=String[]) -> GitHubPages

Add GitHubPages to a template's plugins to add Documenter support via GitHub Pages, including automatic uploading of documentation from TravisCI. Also adds appropriate badges to the README, and updates the .gitignore accordingly.

Keyword Arguments

  • assets::Vector{<:AbstractString}=String[]: Array of paths to Documenter asset files.
Note

If deploying documentation with Travis CI, don't forget to complete the required configuration (see here).

source
GitLabPages(; assets::Vector{<:AbstractString}=String[]) -> GitLabPages

Add GitLabPages to a template's plugins to add Documenter support via GitLab Pages, including automatic uploading of documentation from GitLabCI. Also adds appropriate badges to the README, and updates the .gitignore accordingly.

Keyword Arguments

  • assets::Vector{<:AbstractString}=String[]: Array of paths to Documenter asset files.
source
diff --git a/dev/search/index.html b/dev/search/index.html index d85c91a..406454a 100644 --- a/dev/search/index.html +++ b/dev/search/index.html @@ -1,2 +1,2 @@ -Search · PkgTemplates.jl

Search

Search

Number of results: loading...

+Search · PkgTemplates.jl

Search

Search

Number of results: loading...

diff --git a/dev/search_index.js b/dev/search_index.js index be1fd55..5c1668a 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,531 +1,3 @@ -var documenterSearchIndex = {"docs": [ - -{ - "location": "#", - "page": "Home", - "title": "Home", - "category": "page", - "text": "" -}, - -{ - "location": "#PkgTemplates-1", - "page": "Home", - "title": "PkgTemplates", - "category": "section", - "text": "(Image: Stable) (Image: Latest) (Image: Build Status) (Image: Build Status) (Image: Codecov)PkgTemplates is a Julia package for creating new Julia packages in an easy, repeatable, and customizable way." -}, - -{ - "location": "#Installation-1", - "page": "Home", - "title": "Installation", - "category": "section", - "text": "pkg> add PkgTemplates" -}, - -{ - "location": "#Plugins-1", - "page": "Home", - "title": "Plugins", - "category": "section", - "text": "PkgTemplates is based on plugins which handle the setup of individual package components. The available plugins are:Continuous Integration (CI)\nTravis CI (Linux, MacOS)\nAppVeyor (Windows)\nGitLabCI (Linux)\nCirrusCI (FreeBSD)\nCode Coverage\nCodecov\nCoveralls\nDocumentation\nGitHubPages\nCitation" -}, - -{ - "location": "#Usage-1", - "page": "Home", - "title": "Usage", - "category": "section", - "text": "using LibGit2: getconfig\nisempty(getconfig(\"user.name\", \"\")) && run(`git config --global user.name \"Travis\"`)\nisempty(getconfig(\"user.email\", \"\")) && run(`git config --global user.email \"travis@c.i\"`)\nisempty(getconfig(\"github.user\", \"\")) && run(`git config --global github.user \"travis\"`)\nusing Pkg\nPkg.activate(mktempdir())\n# This code gets run in docs/build/, so this path evaluates to the repo root.\nPkg.add(PackageSpec(path=dirname(dirname(pwd()))))Assuming you have the relatively standard Git options user.name, user.email and github.user set up globally with git config --global, the simplest template requires no arguments:using PkgTemplates\nt = Template()\ngenerate(\"MyPkg\", t)\nrun(`git -C $(joinpath(t.dir, \"MyPkg\")) ls-files`);However, we can also configure a number of keyword arguments to Template:using PkgTemplates\nt = Template(;\n user=\"myusername\",\n license=\"MIT\",\n authors=[\"Chris de Graaf\", \"Invenia Technical Computing Corporation\"],\n dir=\"~/code\",\n julia_version=v\"0.7\",\n ssh=true,\n plugins=[\n TravisCI(),\n Codecov(),\n Coveralls(),\n AppVeyor(),\n GitHubPages(),\n CirrusCI(),\n ],\n)\ngenerate(\"MyPkg2\", t)\nrun(`git -C $(joinpath(t.dir, \"MyPkg2\")) ls-files`);If that looks like a lot of work, you can also create templates interactively with interactive_template:(Image: asciicast)And if that\'s still too much work for you, you can call interactive_template with fast=true to use default values for everything but username and plugin selection.You can also use generate_interactive to interactively generate a template and then immediately use it to create a new package." -}, - -{ - "location": "#Comparison-to-PkgDev-1", - "page": "Home", - "title": "Comparison to PkgDev", - "category": "section", - "text": "PkgTemplates is similar in functionality to PkgDev\'s generate function. However, PkgTemplates offers more customizability in templates and more extensibility via plugins. For the package registration and release management features that PkgTemplates doesn\'t include, you are encouraged to use AttoBot instead." -}, - -{ - "location": "#Contributing-1", - "page": "Home", - "title": "Contributing", - "category": "section", - "text": "It\'s extremely easy to extend PkgTemplates with new plugins. To get started, check out the plugin development guide." -}, - -{ - "location": "pages/package_generation/#", - "page": "Package Generation", - "title": "Package Generation", - "category": "page", - "text": "CurrentModule = PkgTemplates" -}, - -{ - "location": "pages/package_generation/#Package-Generation-1", - "page": "Package Generation", - "title": "Package Generation", - "category": "section", - "text": "Creating new packages with PkgTemplates revolves around creating a new Template, then calling generate on it." -}, - -{ - "location": "pages/package_generation/#PkgTemplates.Template", - "page": "Package Generation", - "title": "PkgTemplates.Template", - "category": "type", - "text": "Template(; kwargs...) -> Template\n\nRecords common information used to generate a package. If you don\'t wish to manually create a template, you can use interactive_template instead.\n\nKeyword Arguments\n\nuser::AbstractString=\"\": GitHub (or other code hosting service) username. If left unset, it will take the the global git config\'s value (github.user). If that is not set, an ArgumentError is thrown. This is case-sensitive for some plugins, so take care to enter it correctly.\nhost::AbstractString=\"github.com\": URL to the code hosting service where your package will reside. Note that while hosts other than GitHub won\'t cause errors, they are not officially supported and they will cause certain plugins will produce incorrect output.\nlicense::AbstractString=\"MIT\": Name of the package license. If an empty string is given, no license is created. available_licenses can be used to list all available licenses, and show_license can be used to print out a particular license\'s text.\nauthors::Union{AbstractString, Vector{<:AbstractString}}=\"\": Names that appear on the license. Supply a string for one author or an array for multiple. Similarly to user, it will take the value of of the global git config\'s value if it is left unset.\ndir::AbstractString=~/.julia/dev: Directory in which the package will go. Relative paths are converted to absolute ones at template creation time.\njulia_version::VersionNumber=1.0.0: Minimum allowed Julia version.\nssh::Bool=false: Whether or not to use SSH for the git remote. If false HTTPS will be used.\ndev::Bool=true: Whether or not to Pkg.develop generated packages.\nmanifest::Bool=false: Whether or not to commit the Manifest.toml.\nplugins::Vector{<:Plugin}=Plugin[]: A list of Plugins that the package will include.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.interactive_template", - "page": "Package Generation", - "title": "PkgTemplates.interactive_template", - "category": "function", - "text": "interactive_template(; fast::Bool=false) -> Template\n\nInteractively create a Template. If fast is set, defaults will be assumed for all values except username and plugins.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#Template-1", - "page": "Package Generation", - "title": "Template", - "category": "section", - "text": "Template\ninteractive_template" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.generate", - "page": "Package Generation", - "title": "PkgTemplates.generate", - "category": "function", - "text": "generate(pkg::AbstractString, t::Template) -> Nothing\ngenerate(t::Template, pkg::AbstractString) -> Nothing\n\nGenerate a package named pkg from t. If git is false, no Git repository is created.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.generate_interactive", - "page": "Package Generation", - "title": "PkgTemplates.generate_interactive", - "category": "function", - "text": "generate_interactive(pkg::AbstractString; fast::Bool=false, git::Bool=true) -> Template\n\nInteractively create a template, and then generate a package with it. Arguments and keywords are used in the same way as in generate and interactive_template.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#generate-1", - "page": "Package Generation", - "title": "generate", - "category": "section", - "text": "generate\ngenerate_interactive" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.gen_tests", - "page": "Package Generation", - "title": "PkgTemplates.gen_tests", - "category": "function", - "text": "gen_tests(pkg_dir::AbstractString, t::Template) -> Vector{String}\n\nCreate the test entrypoint in pkg_dir.\n\nArguments\n\npkg_dir::AbstractString: The package directory in which the files will be generated\nt::Template: The template whose tests we are generating.\n\nReturns an array of generated file/directory names.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.gen_readme", - "page": "Package Generation", - "title": "PkgTemplates.gen_readme", - "category": "function", - "text": "gen_readme(pkg_dir::AbstractString, t::Template) -> Vector{String}\n\nCreate a README in pkg_dir with badges for each enabled plugin.\n\nArguments\n\npkg_dir::AbstractString: The directory in which the files will be generated.\nt::Template: The template whose README we are generating.\n\nReturns an array of generated file/directory names.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.gen_gitignore", - "page": "Package Generation", - "title": "PkgTemplates.gen_gitignore", - "category": "function", - "text": "gen_gitignore(pkg_dir::AbstractString, t::Template) -> Vector{String}\n\nCreate a .gitignore in pkg_dir.\n\nArguments\n\npkg_dir::AbstractString: The directory in which the files will be generated.\nt::Template: The template whose .gitignore we are generating.\n\nReturns an array of generated file/directory names.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#PkgTemplates.gen_license", - "page": "Package Generation", - "title": "PkgTemplates.gen_license", - "category": "function", - "text": "gen_license(pkg_dir::AbstractString, t::Template) -> Vector{String}\n\nCreate a license in pkg_dir.\n\nArguments\n\npkg_dir::AbstractString: The directory in which the files will be generated.\nt::Template: The template whose LICENSE we are generating.\n\nReturns an array of generated file/directory names.\n\n\n\n\n\n" -}, - -{ - "location": "pages/package_generation/#Helper-Functions-1", - "page": "Package Generation", - "title": "Helper Functions", - "category": "section", - "text": "gen_tests\ngen_readme\ngen_gitignore\ngen_license" -}, - -{ - "location": "pages/plugins/#", - "page": "Plugins", - "title": "Plugins", - "category": "page", - "text": "CurrentModule = PkgTemplates" -}, - -{ - "location": "pages/plugins/#Plugins-1", - "page": "Plugins", - "title": "Plugins", - "category": "section", - "text": "Plugins are the secret sauce behind PkgTemplates\'s customization and extension. This page describes plugins that already exist; for information on writing your own plugins, see Plugin Development." -}, - -{ - "location": "pages/plugins/#PkgTemplates.TravisCI", - "page": "Plugins", - "title": "PkgTemplates.TravisCI", - "category": "type", - "text": "TravisCI(; config_file::Union{AbstractString, Nothing}=\"\") -> TravisCI\n\nAdd TravisCI to a template\'s plugins to add a .travis.yml configuration file to generated repositories, and an appropriate badge to the README.\n\nKeyword Arguments:\n\nconfig_file::Union{AbstractString, Nothing}=\"\": Path to a custom .travis.yml. If nothing is supplied, no file will be generated.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.AppVeyor", - "page": "Plugins", - "title": "PkgTemplates.AppVeyor", - "category": "type", - "text": "AppVeyor(; config_file::Union{AbstractString, Nothing}=\"\") -> AppVeyor\n\nAdd AppVeyor to a template\'s plugins to add a .appveyor.yml configuration file to generated repositories, and an appropriate badge to the README.\n\nKeyword Arguments\n\nconfig_file::Union{AbstractString, Nothing}=\"\": Path to a custom .appveyor.yml. If nothing is supplied, no file will be generated.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.GitLabCI", - "page": "Plugins", - "title": "PkgTemplates.GitLabCI", - "category": "type", - "text": "GitLabCI(; config_file::Union{AbstractString, Nothing}=\"\", coverage::Bool=true) -> GitLabCI\n\nAdd GitLabCI to a template\'s plugins to add a .gitlab-ci.yml configuration file to generated repositories, and appropriate badge(s) to the README.\n\nKeyword Arguments:\n\nconfig_file::Union{AbstractString, Nothing}=\"\": Path to a custom .gitlab-ci.yml. If nothing is supplied, no file will be generated.\ncoverage::Bool=true: Whether or not GitLab CI\'s built-in code coverage analysis should be enabled.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.CirrusCI", - "page": "Plugins", - "title": "PkgTemplates.CirrusCI", - "category": "type", - "text": "CirrusCI(; config_file::Union{AbstractString, Nothing}=\"\") -> CirrusCI\n\nAdd CirrusCI to a template\'s plugins to add a .cirrus.yml configuration file to generated repositories, and an appropriate badge to the README. The default configuration file supports only FreeBSD builds via CirrusCI.jl\n\nKeyword Arguments\n\nconfig_file::Union{AbstractString, Nothing}=\"\": Path to a custom .cirrus.yml. If nothing is supplied, no file will be generated.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#Continuous-Integration-(CI)-1", - "page": "Plugins", - "title": "Continuous Integration (CI)", - "category": "section", - "text": "TravisCI\nAppVeyor\nGitLabCI\nCirrusCI" -}, - -{ - "location": "pages/plugins/#PkgTemplates.Codecov", - "page": "Plugins", - "title": "PkgTemplates.Codecov", - "category": "type", - "text": "Codecov(; config_file::Union{AbstractString, Nothing}=nothing) -> Codecov\n\nAdd Codecov to a template\'s plugins to optionally add a .codecov.yml configuration file to generated repositories, and an appropriate badge to the README. Also updates the .gitignore accordingly.\n\nKeyword Arguments:\n\nconfig_file::Union{AbstractString, Nothing}=nothing: Path to a custom .codecov.yml. If left unset, no file will be generated.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.Coveralls", - "page": "Plugins", - "title": "PkgTemplates.Coveralls", - "category": "type", - "text": "Coveralls(; config_file::Union{AbstractString, Nothing}=nothing) -> Coveralls\n\nAdd Coveralls to a template\'s plugins to optionally add a .coveralls.yml configuration file to generated repositories, and an appropriate badge to the README. Also updates the .gitignore accordingly.\n\nKeyword Arguments:\n\nconfig_file::Union{AbstractString, Nothing}=nothing: Path to a custom .coveralls.yml. If left unset, no file will be generated.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#Code-Coverage-1", - "page": "Plugins", - "title": "Code Coverage", - "category": "section", - "text": "Codecov\nCoveralls" -}, - -{ - "location": "pages/plugins/#PkgTemplates.Documenter", - "page": "Plugins", - "title": "PkgTemplates.Documenter", - "category": "type", - "text": "Add a Documenter subtype to a template\'s plugins to add support for documentation generation via Documenter.jl.\n\nBy default, the plugin generates a minimal index.md and a make.jl file. The make.jl file contains the Documenter.makedocs command with predefined values for modules, format, pages, repo, sitename, and authors.\n\nThe subtype is expected to include the following fields:\n\nassets::Vector{AbstractString}, a list of filenames to be included as the assets\n\nkwarg to makedocs\n\ngitignore::Vector{AbstractString}, a list of files to be added to the .gitignore\n\nIt may optionally include the field additional_kwargs::Union{AbstractDict, NamedTuple} to allow additional kwargs to be added to makedocs.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.GitHubPages", - "page": "Plugins", - "title": "PkgTemplates.GitHubPages", - "category": "type", - "text": "GitHubPages(; assets::Vector{<:AbstractString}=String[]) -> GitHubPages\n\nAdd GitHubPages to a template\'s plugins to add Documenter support via GitHub Pages, including automatic uploading of documentation from TravisCI. Also adds appropriate badges to the README, and updates the .gitignore accordingly.\n\nKeyword Arguments\n\nassets::Vector{<:AbstractString}=String[]: Array of paths to Documenter asset files.\n\nnote: Note\nIf deploying documentation with Travis CI, don\'t forget to complete the required configuration (see here).\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#PkgTemplates.GitLabPages", - "page": "Plugins", - "title": "PkgTemplates.GitLabPages", - "category": "type", - "text": "GitLabPages(; assets::Vector{<:AbstractString}=String[]) -> GitLabPages\n\nAdd GitLabPages to a template\'s plugins to add Documenter support via GitLab Pages, including automatic uploading of documentation from GitLabCI. Also adds appropriate badges to the README, and updates the .gitignore accordingly.\n\nKeyword Arguments\n\nassets::Vector{<:AbstractString}=String[]: Array of paths to Documenter asset files.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugins/#Documentation-1", - "page": "Plugins", - "title": "Documentation", - "category": "section", - "text": "Documenter\nGitHubPages\nGitLabPages" -}, - -{ - "location": "pages/plugin_development/#", - "page": "Plugin Development", - "title": "Plugin Development", - "category": "page", - "text": "CurrentModule = PkgTemplates" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.Plugin", - "page": "Plugin Development", - "title": "PkgTemplates.Plugin", - "category": "type", - "text": "A plugin to be added to a Template, which adds some functionality or integration. New plugins should almost always extend GenericPlugin or CustomPlugin.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#Plugin-Development-1", - "page": "Plugin Development", - "title": "Plugin Development", - "category": "section", - "text": "The best and easiest way to contribute to PkgTemplates is to write new plugins.Plugin" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.GenericPlugin", - "page": "Plugin Development", - "title": "PkgTemplates.GenericPlugin", - "category": "type", - "text": "Generic plugins are plugins that add any number of patterns to the generated package\'s .gitignore, and have at most one associated file to generate.\n\nAttributes\n\ngitignore::Vector{AbstractString}: Array of patterns to be added to the .gitignore of generated packages that use this plugin.\nsrc::Union{AbstractString, Nothing}: Path to the file that will be copied into the generated package repository. If set to nothing, no file will be generated. When this defaults to an empty string, there should be a default file in defaults that will be copied. That file\'s name is usually the same as the plugin\'s name, except in all lowercase and with the .yml extension. If this is not the case, an interactive method needs to be implemented to call interactive(; file=\"file.ext\").\ndest::AbstractString: Path to the generated file, relative to the root of the generated package repository.\nbadges::Vector{Badge}: Array of Badges containing information used to create Markdown-formatted badges from the plugin. Entries will be run through substitute, so they may contain placeholder values.\nview::Dict{String, Any}: Additional substitutions to make in both the plugin\'s badges and its associated file. See substitute for details.\n\nExample\n\nstruct MyPlugin <: GenericPlugin\n gitignore::Vector{AbstractString}\n src::Union{AbstractString, Nothing}\n dest::AbstractString\n badges::Vector{Badge}\n view::Dict{String, Any}\n\n function MyPlugin(; config_file::Union{AbstractString, Nothing}=\"\")\n if config_file != nothing\n config_file = if isempty(config_file)\n joinpath(DEFAULTS_DIR, \"my-plugin.toml\")\n elseif isfile(config_file)\n abspath(config_file)\n else\n throw(ArgumentError(\n \"File $(abspath(config_file)) does not exist\"\n ))\n end\n end\n new(\n [\"*.mgp\"],\n config_file,\n \".my-plugin.toml\",\n [\n Badge(\n \"My Plugin\",\n \"https://myplugin.com/badge-{{YEAR}}.png\",\n \"https://myplugin.com/{{USER}}/{{PKGNAME}}.jl\",\n ),\n ],\n Dict{String, Any}(\"YEAR\" => year(today())),\n )\n end\nend\n\ninteractive(::Type{MyPlugin}) = interactive(MyPlugin; file=\"my-plugin.toml\")\n\nThe above plugin ignores files ending with .mgp, copies defaults/my-plugin.toml by default, and creates a badge that links to the project on its own site, using the default substitutions with one addition: {{YEAR}} => year(today()). Since the default config template file doesn\'t follow the generic naming convention, we added another interactive method to correct the assumed filename.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#Generic-Plugins-1", - "page": "Plugin Development", - "title": "Generic Plugins", - "category": "section", - "text": "GenericPlugin" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.CustomPlugin", - "page": "Plugin Development", - "title": "PkgTemplates.CustomPlugin", - "category": "type", - "text": "Custom plugins are plugins whose behaviour does not follow the GenericPlugin pattern. They can implement gen_plugin, badges, and interactive in any way they choose, as long as they conform to the usual type signature.\n\nAttributes\n\ngitignore::Vector{AbstractString}: Array of patterns to be added to the .gitignore of generated packages that use this plugin.\n\nExample\n\nstruct MyPlugin <: CustomPlugin\n gitignore::Vector{AbstractString}\n lucky::Bool\n\n MyPlugin() = new([], rand() > 0.8)\n\n function gen_plugin(p::MyPlugin, t::Template, pkg_name::AbstractString)\n return if p.lucky\n text = substitute(\"You got lucky with {{PKGNAME}}, {{USER}}!\", t)\n gen_file(joinpath(t.dir, pkg_name, \".myplugin.yml\"), text)\n [\".myplugin.yml\"]\n else\n println(\"Maybe next time.\")\n String[]\n end\n end\n\n function badges(p::MyPlugin, user::AbstractString, pkg_name::AbstractString)\n return if p.lucky\n [\n format(Badge(\n \"You got lucky!\",\n \"https://myplugin.com/badge.png\",\n \"https://myplugin.com/$user/$pkg_name.jl\",\n )),\n ]\n else\n String[]\n end\n end\nend\n\ninteractive(:Type{MyPlugin}) = MyPlugin()\n\nThis plugin doesn\'t do much, but it demonstrates how gen_plugin, badges and interactive can be implemented using substitute, gen_file, Badge, and format.\n\nDefining Template Files\n\nOften, the contents of the config file that your plugin generates depends on variables like the package name, the user\'s username, etc. Template files (which are stored in defaults) can use here\'s syntax to define replacements.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#Custom-Plugins-1", - "page": "Plugin Development", - "title": "Custom Plugins", - "category": "section", - "text": "CustomPlugin" -}, - -{ - "location": "pages/plugin_development/#CustomPlugin-Required-Methods-1", - "page": "Plugin Development", - "title": "CustomPlugin Required Methods", - "category": "section", - "text": "" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.gen_plugin", - "page": "Plugin Development", - "title": "PkgTemplates.gen_plugin", - "category": "function", - "text": "gen_plugin(p::Plugin, t::Template, pkg_name::AbstractString) -> Vector{String}\n\nGenerate any files associated with a plugin.\n\nArguments\n\np::Plugin: Plugin whose files are being generated.\nt::Template: Template configuration.\npkg_name::AbstractString: Name of the package.\n\nReturns an array of generated file/directory names.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.interactive", - "page": "Plugin Development", - "title": "PkgTemplates.interactive", - "category": "function", - "text": "interactive(T::Type{<:Plugin}; file::Union{AbstractString, Nothing}=\"\") -> Plugin\n\nInteractively create a plugin of type T, where file is the plugin type\'s default config template with a non-standard name (for MyPlugin, this is anything but \"myplugin.yml\").\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#gen_plugin-1", - "page": "Plugin Development", - "title": "gen_plugin", - "category": "section", - "text": "gen_plugin\ninteractiveNote: interactive is not strictly required, however without it, your custom plugin will not be available when creating templates with interactive_template." -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.badges", - "page": "Plugin Development", - "title": "PkgTemplates.badges", - "category": "function", - "text": "badges(p::Plugin, user::AbstractString, pkg_name::AbstractString) -> Vector{String}\n\nGenerate Markdown badges for the plugin.\n\nArguments\n\np::Plugin: Plugin whose badges we are generating.\nuser::AbstractString: Username of the package creator.\npkg_name::AbstractString: Name of the package.\n\nReturns an array of Markdown badges.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#badges-1", - "page": "Plugin Development", - "title": "badges", - "category": "section", - "text": "badges" -}, - -{ - "location": "pages/plugin_development/#Helper-Types/Functions-1", - "page": "Plugin Development", - "title": "Helper Types/Functions", - "category": "section", - "text": "" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.gen_file", - "page": "Plugin Development", - "title": "PkgTemplates.gen_file", - "category": "function", - "text": "gen_file(file::AbstractString, text::AbstractString) -> Int\n\nCreate a new file containing some given text. Always ends the file with a newline.\n\nArguments\n\nfile::AbstractString: Path to the file to be created.\ntext::AbstractString: Text to write to the file.\n\nReturns the number of bytes written to the file.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#gen_file-1", - "page": "Plugin Development", - "title": "gen_file", - "category": "section", - "text": "gen_file" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.substitute", - "page": "Plugin Development", - "title": "PkgTemplates.substitute", - "category": "function", - "text": "substitute(template::AbstractString, view::Dict{String, Any}) -> String\nsubstitute(\n template::AbstractString,\n pkg_template::Template;\n view::Dict{String, Any}=Dict{String, Any}(),\n) -> String\n\nReplace placeholders in template with values in view via Mustache. template is not modified. If pkg_template is supplied, some default replacements are also performed.\n\nFor information on how to structure template, see \"Defining Template Files\" section in Custom Plugins.\n\nNote: Conditionals in template without a corresponding key in view won\'t error, but will simply be evaluated as false.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#substitute-1", - "page": "Plugin Development", - "title": "substitute", - "category": "section", - "text": "substitute" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.Badge", - "page": "Plugin Development", - "title": "PkgTemplates.Badge", - "category": "type", - "text": "Badge(hover::AbstractString, image::AbstractString, link::AbstractString) -> Badge\n\nA Badge contains the data necessary to generate a Markdown badge.\n\nArguments\n\nhover::AbstractString: Text to appear when the mouse is hovered over the badge.\nimage::AbstractString: URL to the image to display.\nlink::AbstractString: URL to go to upon clicking the badge.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#Badge-1", - "page": "Plugin Development", - "title": "Badge", - "category": "section", - "text": "Badge" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.format", - "page": "Plugin Development", - "title": "PkgTemplates.format", - "category": "function", - "text": "format(b::Badge) -> String\n\nReturn badge\'s data formatted as a Markdown string.\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#format-1", - "page": "Plugin Development", - "title": "format", - "category": "section", - "text": "format" -}, - -{ - "location": "pages/plugin_development/#PkgTemplates.version_floor", - "page": "Plugin Development", - "title": "PkgTemplates.version_floor", - "category": "function", - "text": "version_floor(v::VersionNumber=VERSION) -> String\n\nFormat the given Julia version.\n\nKeyword arguments\n\nv::VersionNumber=VERSION: Version to floor.\n\nReturns \"major.minor\" for the most recent release version relative to v. For prereleases with v.minor == v.patch == 0, returns \"major.minor-\".\n\n\n\n\n\n" -}, - -{ - "location": "pages/plugin_development/#version_floor-1", - "page": "Plugin Development", - "title": "version_floor", - "category": "section", - "text": "version_floor" -}, - -{ - "location": "pages/licenses/#", - "page": "Licenses", - "title": "Licenses", - "category": "page", - "text": "CurrentModule = PkgTemplates" -}, - -{ - "location": "pages/licenses/#PkgTemplates.available_licenses", - "page": "Licenses", - "title": "PkgTemplates.available_licenses", - "category": "function", - "text": "available_licenses([io::IO]) -> Nothing\n\nPrint the names of all available licenses.\n\n\n\n\n\n" -}, - -{ - "location": "pages/licenses/#PkgTemplates.show_license", - "page": "Licenses", - "title": "PkgTemplates.show_license", - "category": "function", - "text": "show_license([io::IO], license::AbstractString) -> Nothing\n\nPrint the text of license. Errors if the license is not found.\n\n\n\n\n\n" -}, - -{ - "location": "pages/licenses/#Licenses-1", - "page": "Licenses", - "title": "Licenses", - "category": "section", - "text": "Many open-source licenses are available for use with PkgTemplates, but if you see that one is missing, don\'t hesitate to open an issue or PR.available_licenses\nshow_license" -}, - -{ - "location": "pages/licenses/#PkgTemplates.read_license", - "page": "Licenses", - "title": "PkgTemplates.read_license", - "category": "function", - "text": "read_license(license::AbstractString) -> String\n\nReturns the contents of license. Errors if the license is not found. Use available_licenses to view available licenses.\n\n\n\n\n\n" -}, - -{ - "location": "pages/licenses/#Helper-Functions-1", - "page": "Licenses", - "title": "Helper Functions", - "category": "section", - "text": "read_license" -}, - -{ - "location": "pages/#", - "page": "Index", - "title": "Index", - "category": "page", - "text": "" -}, - -{ - "location": "pages/#Index-1", - "page": "Index", - "title": "Index", - "category": "section", - "text": "" -}, - -]} +var documenterSearchIndex = {"docs": +[{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"CurrentModule = PkgTemplates","category":"page"},{"location":"developer/#PkgTemplates-Developer-Guide-1","page":"Developer Guide","title":"PkgTemplates Developer Guide","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Pages = [\"developer.md\"]","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"PkgTemplates can be easily extended by adding new Plugins.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"There are two types of plugins: Plugin and BasicPlugin.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Plugin\nBasicPlugin","category":"page"},{"location":"developer/#PkgTemplates.Plugin","page":"Developer Guide","title":"PkgTemplates.Plugin","text":"Plugins are PkgTemplates' source of customization and extensibility. Add plugins to your Templates to enable extra pieces of repository setup.\n\nWhen implementing a new plugin, subtype this type to have full control over its behaviour.\n\n\n\n\n\n","category":"type"},{"location":"developer/#PkgTemplates.BasicPlugin","page":"Developer Guide","title":"PkgTemplates.BasicPlugin","text":"A simple plugin that, in general, creates a single file.\n\n\n\n\n\n","category":"type"},{"location":"developer/#Template-Package-Creation-Pipeline-1","page":"Developer Guide","title":"Template + Package Creation Pipeline","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The Template constructor basically does this:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"- extract values from keyword arguments\n- create a Template from the values\n- for each plugin:\n - validate plugin against the template","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The plugin validation step uses the validate function. It lets us catch mistakes before we try to generate packages.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"validate","category":"page"},{"location":"developer/#PkgTemplates.validate","page":"Developer Guide","title":"PkgTemplates.validate","text":"validate(::Plugin, ::Template)\n\nPerform any required validation for a Plugin.\n\nIt is preferred to do validation here instead of in prehook, because this function is called at Template construction time, whereas the prehook is only run at package generation time.\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The package generation process looks like this:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"- create empty directory for the package\n- for each plugin, ordered by priority:\n - run plugin prehook\n- for each plugin, ordered by priority:\n - run plugin hook\n- for each plugin, ordered by priority:\n - run plugin posthook","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"As you can tell, plugins play a central role in setting up a package.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The three main entrypoints for plugins to do work are the prehook, the hook, and the posthook. As the names might imply, they basically mean \"before the main stage\", \"the main stage\", and \"after the main stage\", respectively.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Each stage is basically identical, since the functions take the exact same arguments. However, the multiple stages allow us to depend on artifacts of the previous stages. For example, the Git plugin uses posthook to commit all generated files, but it wouldn't make sense to do that before the files are generated.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"But what about dependencies within the same stage? In this case, we have priority to define which plugins go when. The Git plugin also uses this function to lower its posthook's priority, so that even if other plugins generate files in their posthooks, they still get committed (provided that those plugins didn't set an even lower priority).","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"prehook\nhook\nposthook\npriority","category":"page"},{"location":"developer/#PkgTemplates.prehook","page":"Developer Guide","title":"PkgTemplates.prehook","text":"prehook(::Plugin, ::Template, pkg_dir::AbstractString)\n\nStage 1 of the package generation process (the \"before\" stage, in general). At this point, pkg_dir is an empty directory that will eventually contain the package, and neither the hooks nor the posthooks have run.\n\nnote: Note\npkg_dir only stays empty until the first plugin chooses to create a file. See also: priority.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.hook","page":"Developer Guide","title":"PkgTemplates.hook","text":"hook(::Plugin, ::Template, pkg_dir::AbstractString)\n\nStage 2 of the package generation pipeline (the \"main\" stage, in general). At this point, the prehooks have run, but not the posthooks.\n\npkg_dir is the directory in which the package is being generated (so basename(pkg_dir) is the package name).\n\nnote: Note\nYou usually shouldn't implement this function for BasicPlugins. If you do, it should probably invoke the generic method (otherwise, there's not much reason to subtype BasicPlugin).\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.posthook","page":"Developer Guide","title":"PkgTemplates.posthook","text":"posthook(::Plugin, ::Template, pkg_dir::AbstractString)\n\nStage 3 of the package generation pipeline (the \"after\" stage, in general). At this point, both the prehooks and hooks have run.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.priority","page":"Developer Guide","title":"PkgTemplates.priority","text":"priority(::Plugin, ::Union{typeof(prehook), typeof(hook), typeof(posthook)}) -> Int\n\nDetermines the order in which plugins are processed (higher goes first). The default priority (DEFAULT_PRIORITY), is 1000.\n\nYou can implement this function per-stage (by using ::typeof(hook), for example), or for all stages by simply using ::Function.\n\n\n\n\n\n","category":"function"},{"location":"developer/#Plugin-Walkthrough-1","page":"Developer Guide","title":"Plugin Walkthrough","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Concrete types that subtype Plugin directly are free to do almost anything. To understand how they're implemented, let's look at simplified versions of two plugins: Documenter to explore templating, and Git to further clarify the multi-stage pipeline.","category":"page"},{"location":"developer/#Example:-Documenter-1","page":"Developer Guide","title":"Example: Documenter","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"@with_kw_noshow struct Documenter <: Plugin\n make_jl::String = default_file(\"docs\", \"make.jl\") <- \"Path to make.jl template\"\n index_md::String = default_file(\"docs\", \"src\", \"index.md\") <- \"Path to index.md template\"\nend\n\ngitignore(::Documenter) = [\"/docs/build/\"]\n\nbadges(::Documenter) = [\n Badge(\n \"Stable\",\n \"https://img.shields.io/badge/docs-stable-blue.svg\",\n \"https://{{{USER}}}.github.io/{{{PKG}}}.jl/stable\",\n ),\n Badge(\n \"Dev\",\n \"https://img.shields.io/badge/docs-dev-blue.svg\",\n \"https://{{{USER}}}.github.io/{{{PKG}}}.jl/dev\",\n ),\n]\n\nview(p::Documenter, t::Template, pkg::AbstractString) = Dict(\n \"AUTHORS\" => join(t.authors, \", \"),\n \"PKG\" => pkg,\n \"REPO\" => \"$(t.host)/$(t.user)/$pkg.jl\",\n \"USER\" => t.user,\n)\n\nfunction hook(p::Documenter, t::Template, pkg_dir::AbstractString)\n pkg = basename(pkg_dir)\n docs_dir = joinpath(pkg_dir, \"docs\")\n\n make = render_file(p.make_jl, combined_view(p, t, pkg), tags(p))\n gen_file(joinpath(docs_dir, \"make.jl\"), make)\n \n index = render_file(p.index_md, combined_view(p, t, pkg), tags(p))\n gen_file(joinpath(docs_dir, \"src\", \"index.md\"), index)\n\n # What this function does is not relevant here.\n create_documentation_project()\nend","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The @with_kw_noshow macro defines keyword constructors for us. Inside of our struct definition, we're using default_file to refer to files in this repository.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"default_file","category":"page"},{"location":"developer/#PkgTemplates.default_file","page":"Developer Guide","title":"PkgTemplates.default_file","text":"default_file(paths::AbstractString...) -> String\n\nReturn a path relative to the default template file directory (~/build/invenia/PkgTemplates.jl/templates).\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The first method we implement for Documenter is gitignore, so that packages created with this plugin ignore documentation build artifacts.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"gitignore","category":"page"},{"location":"developer/#PkgTemplates.gitignore","page":"Developer Guide","title":"PkgTemplates.gitignore","text":"gitignore(::Plugin) -> Vector{String}\n\nReturn patterns that should be added to .gitignore. These are used by the Git plugin.\n\nBy default, an empty list is returned.\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Second, we implement badges to add a couple of badges to new packages' README files.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"badges\nBadge","category":"page"},{"location":"developer/#PkgTemplates.badges","page":"Developer Guide","title":"PkgTemplates.badges","text":"badges(::Plugin) -> Union{Badge, Vector{Badge}}\n\nReturn a list of Badges, or just one, to be added to README.md. These are used by the Readme plugin to add badges to the README.\n\nBy default, an empty list is returned.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.Badge","page":"Developer Guide","title":"PkgTemplates.Badge","text":"Badge(hover::AbstractString, image::AbstractString, link::AbstractString)\n\nContainer for Markdown badge data. Each argument can contain placeholders, which will be filled in with values from combined_view.\n\nArguments\n\nhover::AbstractString: Text to appear when the mouse is hovered over the badge.\nimage::AbstractString: URL to the image to display.\nlink::AbstractString: URL to go to upon clicking the badge.\n\n\n\n\n\n","category":"type"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"These two functions, gitignore and badges, are currently the only \"special\" functions for cross-plugin interactions. In other cases, you can still access the Template's plugins to depend on the presence/properties of other plugins, although that's less powerful.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Third, we implement view, which is used to fill placeholders in badges and rendered files.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"view","category":"page"},{"location":"developer/#PkgTemplates.view","page":"Developer Guide","title":"PkgTemplates.view","text":"view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}\n\nReturn the view to be passed to the text templating engine for this plugin. pkg is the name of the package being generated.\n\nFor BasicPlugins, this is used for both the plugin badges (see badges) and the template file (see source). For other Plugins, it is used only for badges, but you can always call it yourself as part of your hook implementation.\n\nBy default, an empty Dict is returned.\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Finally, we implement hook, which is the real workhorse for the plugin. Inside of this function, we generate a couple of files with the help of a few more text templating functions.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"render_file\nrender_text\ngen_file\ncombined_view\ntags","category":"page"},{"location":"developer/#PkgTemplates.render_file","page":"Developer Guide","title":"PkgTemplates.render_file","text":"render_file(file::AbstractString view::Dict{<:AbstractString}, tags) -> String\n\nRender a template file with the data in view. tags should be a tuple of two strings, which are the opening and closing delimiters, or nothing to use the default delimiters.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.render_text","page":"Developer Guide","title":"PkgTemplates.render_text","text":"render_text(text::AbstractString, view::Dict{<:AbstractString}, tags=nothing) -> String\n\nRender some text with the data in view. tags should be a tuple of two strings, which are the opening and closing delimiters, or nothing to use the default delimiters.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.gen_file","page":"Developer Guide","title":"PkgTemplates.gen_file","text":"gen_file(file::AbstractString, text::AbstractString)\n\nCreate a new file containing some given text. Trailing whitespace is removed, and the file will end with a newline.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.combined_view","page":"Developer Guide","title":"PkgTemplates.combined_view","text":"combined_view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}\n\nThis function combines view and user_view for use in text templating. If you're doing manual file creation or text templating (i.e. writing Plugins that are not BasicPlugins), then you should use this function rather than either of the former two.\n\nnote: Note\nDo not implement this function yourself! If you're implementing a plugin, you should implement view. If you're customizing a plugin as a user, you should implement user_view.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.tags","page":"Developer Guide","title":"PkgTemplates.tags","text":"tags(::Plugin) -> Tuple{String, String}\n\nReturn the delimiters used for text templating. See the Citation plugin for a rare case where changing the tags is necessary.\n\nBy default, the tags are \"{{\" and \"}}\".\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"For more information on text templating, see the BasicPlugin Walkthrough and the section on Custom Template Files.","category":"page"},{"location":"developer/#Example:-Git-1","page":"Developer Guide","title":"Example: Git","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"struct Git <: Plugin end\n\npriority(::Git, ::typeof(posthook)) = 5\n\nfunction validate(::Git, ::Template)\n foreach((\"user.name\", \"user.email\")) do k\n if isempty(LibGit2.getconfig(k, \"\"))\n throw(ArgumentError(\"Git: Global Git config is missing required value '$k'\"))\n end\n end\nend\n\nfunction prehook(::Git, t::Template, pkg_dir::AbstractString)\n LibGit2.with(LibGit2.init(pkg_dir)) do repo\n LibGit2.commit(repo, \"Initial commit\")\n pkg = basename(pkg_dir)\n url = \"https://$(t.host)/$(t.user)/$pkg.jl\"\n close(GitRemote(repo, \"origin\", url))\n end\nend\n\nfunction hook(::Git, t::Template, pkg_dir::AbstractString)\n ignore = mapreduce(gitignore, append!, t.plugins)\n unique!(sort!(ignore))\n gen_file(joinpath(pkg_dir, \".gitignore\"), join(ignore, \"\\n\"))\nend\n\nfunction posthook(::Git, ::Template, pkg_dir::AbstractString)\n LibGit2.with(GitRepo(pkg_dir)) do repo\n LibGit2.add!(repo, \".\")\n LibGit2.commit(repo, \"Files generated by PkgTemplates\")\n end\nend","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Validation and all three hooks are implemented:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"validate makes sure that all required Git configuration is present.\nprehook creates the Git repository for the package.\nhook generates the .gitignore file, using the special gitignore function.\nposthook adds and commits all the generated files.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"As previously mentioned, we use priority to make sure that we wait until all other plugins are finished their work before committing files.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Hopefully, this demonstrates the level of control you have over the package generation process when developing plugins, and when it makes sense to exercise that power!","category":"page"},{"location":"developer/#BasicPlugin-Walkthrough-1","page":"Developer Guide","title":"BasicPlugin Walkthrough","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Most of the time, you don't really need all of the control that we showed off above. Plugins that subtype BasicPlugin perform a much more limited task. In general, they just generate one templated file.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"To illustrate, let's look at the Citation plugin, which creates a CITATION.bib file.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"@with_kw_noshow struct Citation <: BasicPlugin\n file::String = default_file(\"CITATION.bib\")\nend\n\nsource(p::Citation) = p.file\ndestination(::Citation) = \"CITATION.bib\"\n\ntags(::Citation) = \"<<\", \">>\"\n\nview(::Citation, t::Template, pkg::AbstractString) = Dict(\n \"AUTHORS\" => join(t.authors, \", \"),\n \"MONTH\" => month(today()),\n \"PKG\" => pkg,\n \"URL\" => \"https://$(t.host)/$(t.user)/$pkg.jl\",\n \"YEAR\" => year(today()),\n)","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Similar to the Documenter example above, we're defining a keyword constructor, and assigning a default template file from this repository. This plugin adds nothing to .gitignore, and it doesn't add any badges, so implementations for gitignore and badges are omitted.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"First, we implement source and destination to define where the template file comes from, and where it goes. These functions are specific to BasicPlugins, and have no effect on regular Plugins by default.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"source\ndestination","category":"page"},{"location":"developer/#PkgTemplates.source","page":"Developer Guide","title":"PkgTemplates.source","text":"source(::BasicPlugin) -> Union{String, Nothing}\n\nReturn the path to a plugin's template file, or nothing to indicate no file.\n\nBy default, nothing is returned.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.destination","page":"Developer Guide","title":"PkgTemplates.destination","text":"destination(::BasicPlugin) -> String\n\nReturn the destination, relative to the package root, of a plugin's configuration file.\n\nThis function must be implemented.\n\n\n\n\n\n","category":"function"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Next, we implement tags. We briefly saw this function earlier, but in this case it's necessary to change its behaviour from the default. To see why, it might help to see the template file in its entirety:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"@misc{<<&PKG>>.jl,\n\tauthor = {<<&AUTHORS>>},\n\ttitle = {<<&PKG>>.jl},\n\turl = {<<&URL>>},\n\tversion = {v0.1.0},\n\tyear = {<<&YEAR>>},\n\tmonth = {<<&MONTH>>}\n}","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Because the file contains its own {} delimiters, we need to use different ones for templating to work properly.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Finally, we implement view to fill in the placeholders that we saw in the template file.","category":"page"},{"location":"developer/#Doing-Extra-Work-With-BasicPlugins-1","page":"Developer Guide","title":"Doing Extra Work With BasicPlugins","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Notice that we didn't have to implement hook for our plugin. It's implemented for all BasicPlugins, like so:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"function render_plugin(p::BasicPlugin, t::Template, pkg::AbstractString)\n return render_file(source(p), combined_view(p, t, pkg), tags(p))\nend\n\nfunction hook(p::BasicPlugin, t::Template, pkg_dir::AbstractString)\n source(p) === nothing && return\n pkg = basename(pkg_dir)\n path = joinpath(pkg_dir, destination(p))\n text = render_plugin(p, t, pkg)\n gen_file(path, text)\nend","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"But what if we want to do a little more than just generate one file?","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"A good example of this is the Tests plugin. It creates runtests.jl, but it also modifies the Project.toml to include the Test dependency.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"Of course, we could use a normal Plugin, but it turns out there's a way to avoid that while still getting the extra capbilities that we want.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"The plugin implements its own hook, but uses invoke to avoid duplicating the file creation code:","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"@with_kw_noshow struct Tests <: BasicPlugin\n file::String = default_file(\"runtests.jl\")\nend\n\nsource(p::Tests) = p.file\ndestination(::Tests) = joinpath(\"test\", \"runtests.jl\")\nview(::Tests, ::Template, pkg::AbstractString) = Dict(\"PKG\" => pkg)\n\nfunction hook(p::Tests, t::Template, pkg_dir::AbstractString)\n # Do the normal BasicPlugin behaviour to create the test script.\n invoke(hook, Tuple{BasicPlugin, Template, AbstractString}, p, t, pkg_dir)\n # Do some other work.\n add_test_dependency()\nend","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"There is also a default validate implementation for BasicPlugins, which checks that the plugin's source file exists, and throws an ArgumentError otherwise. If you want to extend the validation but keep the file existence check, use the invoke method as described above.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"For more examples, see the plugins in the Continuous Integration (CI) and Code Coverage sections.","category":"page"},{"location":"developer/#Miscellaneous-Tips-1","page":"Developer Guide","title":"Miscellaneous Tips","text":"","category":"section"},{"location":"developer/#Writing-Template-Files-1","page":"Developer Guide","title":"Writing Template Files","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"For an overview of writing template files for Mustache.jl, see Custom Template Files in the user guide.","category":"page"},{"location":"developer/#Predicates-1","page":"Developer Guide","title":"Predicates","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"There are a few predicate functions for plugins that are occasionally used to answer questions like \"does this Template have any code coverage plugins?\". If you're implementing a plugin that fits into one of the following categories, it would be wise to implement the corresponding predicate function to return true for instances of your type.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"needs_username\nis_ci\nis_coverage","category":"page"},{"location":"developer/#PkgTemplates.needs_username","page":"Developer Guide","title":"PkgTemplates.needs_username","text":"needs_username(::Plugin) -> Bool\n\nDetermine whether or not a plugin needs a Git hosting service username to function correctly. If you are implementing a plugin that uses the user field of a Template, you should implement this function and return true.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.is_ci","page":"Developer Guide","title":"PkgTemplates.is_ci","text":"is_ci(::Plugin) -> Bool\n\nDetermine whether or not a plugin is a CI plugin. If you are adding a CI plugin, you should implement this function and return true.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.is_coverage","page":"Developer Guide","title":"PkgTemplates.is_coverage","text":"is_coverage(::Plugin) -> Bool\n\nDetermine whether or not a plugin is a coverage plugin. If you are adding a coverage plugin, you should implement this function and return true.\n\n\n\n\n\n","category":"function"},{"location":"developer/#Formatting-Version-Numbers-1","page":"Developer Guide","title":"Formatting Version Numbers","text":"","category":"section"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"When writing configuration files for CI services, working with version numbers is often needed. There are a few convenience functions that can be used to make this a little bit easier.","category":"page"},{"location":"developer/#","page":"Developer Guide","title":"Developer Guide","text":"compat_version\nformat_version\ncollect_versions","category":"page"},{"location":"developer/#PkgTemplates.compat_version","page":"Developer Guide","title":"PkgTemplates.compat_version","text":"compat_version(v::VersionNumber) -> String\n\nFormat a VersionNumber to exclude trailing zero components.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.format_version","page":"Developer Guide","title":"PkgTemplates.format_version","text":"format_version(v::Union{VersionNumber, AbstractString}) -> String\n\nStrip everything but the major and minor release from a VersionNumber. Strings are left in their original form.\n\n\n\n\n\n","category":"function"},{"location":"developer/#PkgTemplates.collect_versions","page":"Developer Guide","title":"PkgTemplates.collect_versions","text":"collect_versions(t::Template, versions::Vector) -> Vector{String}\n\nCombine t's Julia version with versions, and format them as major.minor. This is useful for creating lists of versions to be included in CI configurations.\n\n\n\n\n\n","category":"function"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"CurrentModule = PkgTemplates","category":"page"},{"location":"user/#PkgTemplates-User-Guide-1","page":"User Guide","title":"PkgTemplates User Guide","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Pages = [\"user.md\"]","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Using PkgTemplates is straightforward. Just create a Template, and call it on a package name to generate that package:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"using PkgTemplates\nt = Template()\nt(\"MyPkg\")","category":"page"},{"location":"user/#Template-1","page":"User Guide","title":"Template","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Template","category":"page"},{"location":"user/#PkgTemplates.Template","page":"User Guide","title":"PkgTemplates.Template","text":"Template(; kwargs...)\n\nA configuration used to generate packages.\n\nKeyword Arguments\n\nUser Options\n\nuser::AbstractString=\"username\": GitHub (or other code hosting service) username. The default value comes from the global Git config (github.user). If no value is obtained, many plugins that use this value will not work.\nauthors::Union{AbstractString, Vector{<:AbstractString}}=\"name \": Package authors. Like user, it takes its default value from the global Git config (user.name and user.email).\n\nPackage Options\n\ndir::AbstractString=\"~/.julia/dev\": Directory to place packages in.\nhost::AbstractString=\"github.com\": URL to the code hosting service where packages will reside.\njulia::VersionNumber=v\"1.0.0\": Minimum allowed Julia version.\n\nTemplate Plugins\n\nplugins::Vector{<:Plugin}=Plugin[]: A list of Plugins used by the template.\ndisable_defaults::Vector{DataType}=DataType[]: Default plugins to disable. The default plugins are ProjectFile, SrcDir, Tests, Readme, License, and Git. To override a default plugin instead of disabling it altogether, supply it via plugins.\n\n\n\nTo create a package from a Template, use the following syntax:\n\njulia> t = Template();\n\njulia> t(\"PkgName\")\n\n\n\n\n\n","category":"type"},{"location":"user/#Plugins-1","page":"User Guide","title":"Plugins","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Plugins add functionality to Templates. There are a number of plugins available to automate common boilerplate tasks.","category":"page"},{"location":"user/#Default-Plugins-1","page":"User Guide","title":"Default Plugins","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"These plugins are included by default. They can be overridden by supplying another value via the plugins keyword, or disabled by supplying the type via the disable_defaults keyword.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"ProjectFile\nSrcDir\nTests\nReadme\nLicense\nGit","category":"page"},{"location":"user/#PkgTemplates.ProjectFile","page":"User Guide","title":"PkgTemplates.ProjectFile","text":"ProjectFile(; version=v\"0.1.0\")\n\nCreates a Project.toml.\n\nKeyword Arguments\n\nversion::VersionNumber: The initial version of created packages.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.SrcDir","page":"User Guide","title":"PkgTemplates.SrcDir","text":"SrcDir(; file=\"~/build/invenia/PkgTemplates.jl/templates/src/module.jl\")\n\nCreates a module entrypoint.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for src/.jl.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.Tests","page":"User Guide","title":"PkgTemplates.Tests","text":"Tests(; file=\"~/build/invenia/PkgTemplates.jl/templates/test/runtests.jl\", project=false)\n\nSets up testing for packages.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for runtests.jl.\nproject::Bool: Whether or not to create a new project for tests (test/Project.toml). See here for more details.\n\nnote: Note\nManaging test dependencies with test/Project.toml is only supported in Julia 1.2 and later.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.Readme","page":"User Guide","title":"PkgTemplates.Readme","text":"Readme(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/README.md\",\n destination=\"README.md\",\n inline_badges=false,\n)\n\nCreates a README file that contains badges for other included plugins.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for the README.\ndestination::AbstractString: File destination, relative to the repository root. For example, values of \"README\" or \"README.rst\" might be desired.\ninline_badges::Bool: Whether or not to put the badges on the same line as the package name.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.License","page":"User Guide","title":"PkgTemplates.License","text":"License(; name=\"MIT\", path=nothing, destination=\"LICENSE\")\n\nCreates a license file.\n\nKeyword Arguments\n\nname::AbstractString: Name of a license supported by PkgTemplates. Available licenses can be seen here.\npath::Union{AbstractString, Nothing}: Path to a custom license file. This keyword takes priority over name.\ndestination::AbstractString: File destination, relative to the repository root. For example, \"LICENSE.md\" might be desired.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.Git","page":"User Guide","title":"PkgTemplates.Git","text":"Git(; ignore=String[], ssh=false, manifest=false, gpgsign=false)\n\nCreates a Git repository and a .gitignore file.\n\nKeyword Arguments\n\nignore::Vector{<:AbstractString}: Patterns to add to the .gitignore. See also: gitignore.\nssh::Bool: Whether or not to use SSH for the remote. If left unset, HTTPS is used.\nmanifest::Bool: Whether or not to commit Manifest.toml.\ngpgsign::Bool: Whether or not to sign commits with your GPG key. This option requires that the Git CLI is installed, and for you to have a GPG key associated with your committer identity.\n\n\n\n\n\n","category":"type"},{"location":"user/#Continuous-Integration-(CI)-1","page":"User Guide","title":"Continuous Integration (CI)","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"These plugins will create the configuration files of common CI services for you.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"AppVeyor\nCirrusCI\nDroneCI\nGitHubActions\nGitLabCI\nTravisCI","category":"page"},{"location":"user/#PkgTemplates.AppVeyor","page":"User Guide","title":"PkgTemplates.AppVeyor","text":"AppVeyor(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/appveyor.yml\",\n x86=false,\n coverage=true,\n extra_versions=[\"1.0\", \"1.0\", \"nightly\"],\n)\n\nIntegrates your packages with AppVeyor via AppVeyor.jl.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for .appveyor.yml.\nx86::Bool: Whether or not to run builds on 32-bit systems, in addition to the default 64-bit builds.\ncoverage::Bool: Whether or not to publish code coverage. Codecov must also be included.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.CirrusCI","page":"User Guide","title":"PkgTemplates.CirrusCI","text":"CirrusCI(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/cirrus.yml\",\n image=\"freebsd-12-0-release-amd64\",\n coverage=true,\n extra_versions=[\"1.0\", \"1.0\", \"nightly\"],\n)\n\nIntegrates your packages with Cirrus CI via CirrusCI.jl.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for .cirrus.yml.\nimage::AbstractString: The FreeBSD image to be used.\ncoverage::Bool: Whether or not to publish code coverage. Codecov must also be included.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\nnote: Note\nCode coverage submission from Cirrus CI is not yet supported by Coverage.jl.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.DroneCI","page":"User Guide","title":"PkgTemplates.DroneCI","text":"DroneCI(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/drone.star\",\n amd64=true,\n arm=false,\n arm64=false,\n extra_versions=[\"1.0\", \"1.0\"],\n)\n\nIntegrates your packages with Drone CI.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for .drone.star.\ndestination::AbstractString: File destination, relative to the repository root. For example, you might want to generate a .drone.yml instead of the default Starlark file.\namd64::Bool: Whether or not to run builds on AMD64.\narm::Bool: Whether or not to run builds on ARM (32-bit).\narm64::Bool: Whether or not to run builds on ARM64.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\nnote: Note\nNightly Julia is not supported.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.GitHubActions","page":"User Guide","title":"PkgTemplates.GitHubActions","text":"GitHubActions(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/github/workflows/ci.yml\",\n destination=\"ci.yml\",\n linux=true,\n osx=true,\n windows=true,\n x64=true,\n x86=false,\n coverage=true,\n extra_versions=[\"1.0\", \"1.0\"],\n)\n\nIntegrates your packages with GitHub Actions.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for the workflow file.\ndestination::AbstractString: Destination of the worflow file, relative to .github/workflows.\nlinux::Bool: Whether or not to run builds on Linux.\nosx::Bool: Whether or not to run builds on OSX (MacOS).\nwindows::Bool: Whether or not to run builds on Windows.\nx64::Bool: Whether or not to run builds on 64-bit architecture.\nx86::Bool: Whether or not to run builds on 32-bit architecture.\ncoverage::Bool: Whether or not to publish code coverage. Another code coverage plugin such as Codecov must also be included.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\nnote: Note\nIf using coverage plugins, don't forget to manually add your API tokens as secrets, as described here.\n\nnote: Note\nNightly Julia is not supported.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.GitLabCI","page":"User Guide","title":"PkgTemplates.GitLabCI","text":"GitLabCI(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/gitlab-ci.yml\",\n coverage=true,\n extra_versions=[\"1.0\", \"1.0\"],\n)\n\nIntegrates your packages with GitLab CI.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for .gitlab-ci.yml.\ncoverage::Bool: Whether or not to compute code coverage.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\nGitLab Pages\n\nDocumentation can be generated by including a Documenter{GitLabCI} plugin. See Documenter for more information.\n\nnote: Note\nNightly Julia is not supported.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.TravisCI","page":"User Guide","title":"PkgTemplates.TravisCI","text":"TravisCI(;\n file=\"~/build/invenia/PkgTemplates.jl/templates/travis.yml\",\n linux=true,\n osx=true,\n windows=true,\n x64=true,\n x86=false,\n arm64=false,\n coverage=true,\n extra_versions=[\"1.0\", \"1.0\", \"nightly\"],\n)\n\nIntegrates your packages with Travis CI.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for .travis.yml.\nlinux::Bool: Whether or not to run builds on Linux.\nosx::Bool: Whether or not to run builds on OSX (MacOS).\nwindows::Bool: Whether or not to run builds on Windows.\nx64::Bool: Whether or not to run builds on 64-bit architecture.\nx86::Bool: Whether or not to run builds on 32-bit architecture.\narm64::Bool: Whether or not to run builds on the ARM64 architecture.\ncoverage::Bool: Whether or not to publish code coverage. Another code coverage plugin such as Codecov must also be included.\nextra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.\n\n\n\n\n\n","category":"type"},{"location":"user/#Code-Coverage-1","page":"User Guide","title":"Code Coverage","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"These plugins will enable code coverage reporting from CI.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Codecov\nCoveralls","category":"page"},{"location":"user/#PkgTemplates.Codecov","page":"User Guide","title":"PkgTemplates.Codecov","text":"Codecov(; file=nothing)\n\nSets up code coverage submission from CI to Codecov.\n\nKeyword Arguments\n\nfile::Union{AbstractString, Nothing}: Template file for .codecov.yml, or nothing to create no file.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.Coveralls","page":"User Guide","title":"PkgTemplates.Coveralls","text":"Coveralls(; file=nothing)\n\nSets up code coverage submission from CI to Coveralls.\n\nKeyword Arguments\n\nfile::Union{AbstractString, Nothing}: Template file for .coveralls.yml, or nothing to create no file.\n\n\n\n\n\n","category":"type"},{"location":"user/#Documentation-1","page":"User Guide","title":"Documentation","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Documenter","category":"page"},{"location":"user/#PkgTemplates.Documenter","page":"User Guide","title":"PkgTemplates.Documenter","text":"Documenter{T<:Union{TravisCI, GitLabCI, Nothing}}(;\n make_jl=\"~/build/invenia/PkgTemplates.jl/templates/docs/make.jl\",\n index_md=\"~/build/invenia/PkgTemplates.jl/templates/docs/src/index.md\",\n assets=String[],\n canonical_url=make_canonical(T),\n makedocs_kwargs=Dict{Symbol, Any}(),\n)\n\nSets up documentation generation via Documenter.jl. Documentation deployment depends on T, where T is some supported CI plugin, or Nothing to only support local documentation builds.\n\nSupported Type Parameters\n\nGitHubActions: Deploys documentation to GitHub Pages with the help of GitHubActions.\nTravisCI: Deploys documentation to GitHub Pages with the help of TravisCI.\nGitLabCI: Deploys documentation to GitLab Pages with the help of GitLabCI.\nNothing (default): Does not set up documentation deployment.\n\nKeyword Arguments\n\nmake_jl::AbstractString: Template file for make.jl.\nindex_md::AbstractString: Template file for index.md.\nassets::Vector{<:AbstractString}: Extra assets for the generated site.\ncanonical_url::Union{Function, Nothing}: A function to generate the site's canonical URL. The default value will compute GitHub Pages and GitLab Pages URLs for TravisCI and GitLabCI, respectively. If set to nothing, no canonical URL is set.\nmakedocs_kwargs::Dict{Symbol}: Extra keyword arguments to be inserted into makedocs.\n\nnote: Note\nIf deploying documentation with Travis CI, don't forget to complete the required configuration.\n\n\n\n\n\n","category":"type"},{"location":"user/#Miscellaneous-1","page":"User Guide","title":"Miscellaneous","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Develop\nCitation","category":"page"},{"location":"user/#PkgTemplates.Develop","page":"User Guide","title":"PkgTemplates.Develop","text":"Develop()\n\nAdds generated packages to the current environment by deving them. See the Pkg documentation here for more details.\n\n\n\n\n\n","category":"type"},{"location":"user/#PkgTemplates.Citation","page":"User Guide","title":"PkgTemplates.Citation","text":"Citation(; file=\"~/build/invenia/PkgTemplates.jl/templates/CITATION.bib\", readme=false)\n\nCreates a CITATION.bib file for citing package repositories.\n\nKeyword Arguments\n\nfile::AbstractString: Template file for CITATION.bib.\nreadme::Bool: Whether or not to include a section about citing in the README.\n\n\n\n\n\n","category":"type"},{"location":"user/#A-More-Complicated-Example-1","page":"User Guide","title":"A More Complicated Example","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Here are a few example templates that use the options and plugins explained above.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"This one includes plugins suitable for a project hosted on GitHub, and some other customizations:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Template(; \n user=\"my-username\",\n dir=\"~/code\",\n authors=\"Acme Corp\",\n julia=v\"1.1\",\n plugins=[\n License(; name=\"MPL\"),\n Git(; manifest=true, ssh=true),\n GitHubActions(; x86=true),\n Codecov(),\n Documenter{GitHubActions}(),\n Develop(),\n ],\n)","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Here's one that works well for projects hosted on GitLab:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Template(;\n user=\"my-username\",\n host=\"gitlab.com\",\n plugins=[\n GitLabCI(),\n Documenter{GitLabCI}(),\n ],\n)","category":"page"},{"location":"user/#Custom-Template-Files-1","page":"User Guide","title":"Custom Template Files","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"note: Templates vs Templating\nThis documentation refers plenty to Templates, the package's main type, but it also refers to \"template files\" and \"text templating\", which are plaintext files with placeholders to be filled with data, and the technique of filling those placeholders with data, respectively.These concepts should be familiar if you've used Jinja or Mustache (Mustache is the particular flavour used by PkgTemplates, via Mustache.jl). Please keep the difference between these two things in mind!","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Many plugins support a file argument or similar, which sets the path to the template file to be used for generating files. Each plugin has a sensible default that should make sense for most people, but you might have a specialized workflow that requires a totally different template file.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"If that's the case, a basic understanding of Mustache's syntax is required. Here's an example template file:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Hello, {{{name}}}.\n\n{{#weather}}\nIt's {{{weather}}} outside. \n{{/weather}}\n{{^weather}}\nI don't know what the weather outside is.\n{{/weather}}\n\n{{#has_things}}\nI have the following things:\n{{/has_things}}\n{{#things}}\n- Here's a thing: {{{.}}}\n{{/things}}\n\n{{#people}}\n- {{{name}}} is {{{mood}}}\n{{/people}}","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"In the first section, name is a key, and its value replaces {{{name}}}.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"In the second section, weather's value may or may not exist. If it does exist, then \"It's weather outside\" is printed. Otherwise, \"I don't know what the weather outside is\" is printed. Mustache uses a notion of \"truthiness\" similar to Python or JavaScript, where values of nothing, false, or empty collections are all considered to not exist.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"In the third section, has_things' value is printed if it's truthy. Then, if the things list is truthy (i.e. not empty), its values are each printed on their own line. The reason that we have two separate keys is that {{#things}} iterates over the whole things list, even when there are no {{{.}}} placeholders, which would duplicate \"I have the following things:\" n times.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"The fourth section iterates over the people list, but instead of using the {{{.}}} placeholder, we have name and mood, which are keys or fields of the list elements. Most types are supported here, including Dicts and structs. NamedTuples require you to use {{{:name}}} instead of the normal {{{name}}}, though.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"You might notice that some curlies are in groups of two ({{key}}), and some are in groups of three ({{{key}}}). Whenever we want to subtitute in a value, using the triple curlies disables HTML escaping, which we rarely want for the types of files we're creating. If you do want escaping, just use the double curlies. And if you're using different delimiters, for example <>, use <<&foo>> to disable escaping.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Assuming the following view:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"struct Person; name::String; mood::String; end\nthings = [\"a\", \"b\", \"c\"]\nview = Dict(\n \"name\" => \"Chris\",\n \"weather\" => \"sunny\",\n \"has_things\" => !isempty(things),\n \"things\" => things,\n \"people\" => [Person(\"John\", \"happy\"), Person(\"Jane\", \"sad\")],\n)","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Our example template would produce this:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Hello, Chris.\n\nIt's sunny outside.\n\nI have the following things:\n- Here's a thing: a\n- Here's a thing: b\n- Here's a thing: c\n\n- John is happy\n- Jane is sad","category":"page"},{"location":"user/#Extending-Existing-Plugins-1","page":"User Guide","title":"Extending Existing Plugins","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Most of the existing plugins generate a file from a template file. If you want to use custom template files, you may run into situations where the data passed into the templating engine is not sufficient. In this case, you can look into implementing user_view to supply whatever data is necessary for your use case.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"user_view","category":"page"},{"location":"user/#PkgTemplates.user_view","page":"User Guide","title":"PkgTemplates.user_view","text":"user_view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}\n\nThe same as view, but for use by package users for extension.\n\nValues returned by this function will override those from view when the keys are the same.\n\n\n\n\n\n","category":"function"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"For example, suppose you were using the Readme plugin with a custom template file that looked like this:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"# {{PKG}}\n\nCreated on *{{TODAY}}*.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"The view function supplies a value for PKG, but it does not supply a value for TODAY. Rather than override view, we can implement this function to get both the default values and whatever else we need to add.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"user_view(::Readme, ::Template, ::AbstractString) = Dict(\"TODAY\" => today())","category":"page"},{"location":"user/#Saving-Templates-1","page":"User Guide","title":"Saving Templates","text":"","category":"section"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"One of the main reasons for PkgTemplates' existence is for new packages to be consistent. This means using the same template more than once, so we want a way to save a template to be used later.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Here's my recommendation for loading a template whenever it's needed:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"function template()\n @eval using PkgTemplates\n Template(; #= ... =#)\nend","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Add this to your startup.jl, and you can create your template from anywhere, without incurring any startup cost.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Another strategy is to write the string representation of the template to a Julia file:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"const t = Template(; #= ... =#)\nopen(\"template.jl\", \"w\") do io\n println(io, \"using PkgTemplates\")\n sprint(show, io, t)\nend","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Then the template is just an include away:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"const t = include(\"template.jl\")","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"The only disadvantage to this approach is that the saved template is much less human-readable than code you wrote yourself.","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"One more method of saving templates is to simply use the Serialization package in the standard library:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"const t = Template(; #= ... =#)\nusing Serialization\nopen(io -> serialize(io, t), \"template.bin\", \"w\")","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"Then simply deserialize to load:","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"using Serialization\nconst t = open(deserialize, \"template.bin\")","category":"page"},{"location":"user/#","page":"User Guide","title":"User Guide","text":"This approach has the same disadvantage as the previous one, and the serialization format is not guaranteed to be stable across Julia versions.","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"CurrentModule = PkgTemplates","category":"page"},{"location":"migrating/#Migrating-To-PkgTemplates-0.7-1","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"PkgTemplates 0.7 is a ground-up rewrite of the package with similar functionality but with updated APIs and internals. Here is a summary of things that existed in older versions but have been moved elsewhere or removed. However, it might be easier to just read the User Guide.","category":"page"},{"location":"migrating/#Template-keywords-1","page":"Migrating To PkgTemplates 0.7+","title":"Template keywords","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"The recurring theme is \"everything is a plugin now\".","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Old New\nlicense=\"ISC\" plugins=[License(; name=\"ISC\")]\ndevelop=true * plugins=[Develop()]\ngit=false disable_defaults=[Git]\njulia_version=v\"1\" julia=v\"1\"\nssh=true plugins=[Git(; ssh=true)]\nmanifest=true plugins=[Git(; manifest=true)]","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"* develop=true was the default setting, but it is no longer the default in PkgTemplates 0.7+.","category":"page"},{"location":"migrating/#Plugins-1","page":"Migrating To PkgTemplates 0.7+","title":"Plugins","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Aside from renamings, basically every plugin has had their constructors reworked. So if you are using anything non-default, you should consult the new docstring.","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Old New\nGitHubPages Documenter{TravisCI}\nGitLabPages Documenter{GitLabCI}","category":"page"},{"location":"migrating/#Package-Generation-1","page":"Migrating To PkgTemplates 0.7+","title":"Package Generation","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"One less name to remember!","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Old New\ngenerate(::Template, pkg::AbstractString) (::Template)(pkg::AbstractString)","category":"page"},{"location":"migrating/#Interactive-Templates-1","page":"Migrating To PkgTemplates 0.7+","title":"Interactive Templates","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Currently not implemented, but will be in the future.","category":"page"},{"location":"migrating/#Other-Functions-1","page":"Migrating To PkgTemplates 0.7+","title":"Other Functions","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Two less names to remember! Although it's unlikely that anyone used these.","category":"page"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"Old New\navailable_licenses View licenses on GitHub\nshow_license View licenses on GitHub","category":"page"},{"location":"migrating/#Custom-Plugins-1","page":"Migrating To PkgTemplates 0.7+","title":"Custom Plugins","text":"","category":"section"},{"location":"migrating/#","page":"Migrating To PkgTemplates 0.7+","title":"Migrating To PkgTemplates 0.7+","text":"In addition to the changes in usage, custom plugins from older versions of PkgTemplates will not work in 0.7+. See the Developer Guide for more information on the new extension API.","category":"page"},{"location":"#","page":"Home","title":"Home","text":"CurrentModule = PkgTemplates","category":"page"},{"location":"#PkgTemplates-1","page":"Home","title":"PkgTemplates","text":"","category":"section"},{"location":"#","page":"Home","title":"Home","text":"PkgTemplates creates new Julia packages in an easy, repeatable, and customizable way.","category":"page"},{"location":"#Documentation-1","page":"Home","title":"Documentation","text":"","category":"section"},{"location":"#","page":"Home","title":"Home","text":"If you're looking to create new packages, see the User Guide.","category":"page"},{"location":"#","page":"Home","title":"Home","text":"If you want to create new plugins, see the Developer Guide.","category":"page"},{"location":"#","page":"Home","title":"Home","text":"if you're trying to migrate from an older version of PkgTemplates, see Migrating To PkgTemplates 0.7+.","category":"page"},{"location":"#Index-1","page":"Home","title":"Index","text":"","category":"section"},{"location":"#","page":"Home","title":"Home","text":"","category":"page"}] +} diff --git a/dev/user/index.html b/dev/user/index.html new file mode 100644 index 0000000..e8cee48 --- /dev/null +++ b/dev/user/index.html @@ -0,0 +1,124 @@ + +User Guide · PkgTemplates.jl

User Guide

PkgTemplates User Guide

Using PkgTemplates is straightforward. Just create a Template, and call it on a package name to generate that package:

using PkgTemplates
+t = Template()
+t("MyPkg")

Template

Template(; kwargs...)

A configuration used to generate packages.

Keyword Arguments

User Options

  • user::AbstractString="username": GitHub (or other code hosting service) username. The default value comes from the global Git config (github.user). If no value is obtained, many plugins that use this value will not work.
  • authors::Union{AbstractString, Vector{<:AbstractString}}="name <email>": Package authors. Like user, it takes its default value from the global Git config (user.name and user.email).

Package Options

  • dir::AbstractString="~/.julia/dev": Directory to place packages in.
  • host::AbstractString="github.com": URL to the code hosting service where packages will reside.
  • julia::VersionNumber=v"1.0.0": Minimum allowed Julia version.

Template Plugins

  • plugins::Vector{<:Plugin}=Plugin[]: A list of Plugins used by the template.
  • disable_defaults::Vector{DataType}=DataType[]: Default plugins to disable. The default plugins are ProjectFile, SrcDir, Tests, Readme, License, and Git. To override a default plugin instead of disabling it altogether, supply it via plugins.

To create a package from a Template, use the following syntax:

julia> t = Template();
+
+julia> t("PkgName")
source

Plugins

Plugins add functionality to Templates. There are a number of plugins available to automate common boilerplate tasks.

Default Plugins

These plugins are included by default. They can be overridden by supplying another value via the plugins keyword, or disabled by supplying the type via the disable_defaults keyword.

ProjectFile(; version=v"0.1.0")

Creates a Project.toml.

Keyword Arguments

  • version::VersionNumber: The initial version of created packages.
source
SrcDir(; file="~/build/invenia/PkgTemplates.jl/templates/src/module.jl")

Creates a module entrypoint.

Keyword Arguments

  • file::AbstractString: Template file for src/<module>.jl.
source
Tests(; file="~/build/invenia/PkgTemplates.jl/templates/test/runtests.jl", project=false)

Sets up testing for packages.

Keyword Arguments

  • file::AbstractString: Template file for runtests.jl.
  • project::Bool: Whether or not to create a new project for tests (test/Project.toml). See here for more details.
Note

Managing test dependencies with test/Project.toml is only supported in Julia 1.2 and later.

source
Readme(;
+    file="~/build/invenia/PkgTemplates.jl/templates/README.md",
+    destination="README.md",
+    inline_badges=false,
+)

Creates a README file that contains badges for other included plugins.

Keyword Arguments

  • file::AbstractString: Template file for the README.
  • destination::AbstractString: File destination, relative to the repository root. For example, values of "README" or "README.rst" might be desired.
  • inline_badges::Bool: Whether or not to put the badges on the same line as the package name.
source
License(; name="MIT", path=nothing, destination="LICENSE")

Creates a license file.

Keyword Arguments

  • name::AbstractString: Name of a license supported by PkgTemplates. Available licenses can be seen here.
  • path::Union{AbstractString, Nothing}: Path to a custom license file. This keyword takes priority over name.
  • destination::AbstractString: File destination, relative to the repository root. For example, "LICENSE.md" might be desired.
source
Git(; ignore=String[], ssh=false, manifest=false, gpgsign=false)

Creates a Git repository and a .gitignore file.

Keyword Arguments

  • ignore::Vector{<:AbstractString}: Patterns to add to the .gitignore. See also: gitignore.
  • ssh::Bool: Whether or not to use SSH for the remote. If left unset, HTTPS is used.
  • manifest::Bool: Whether or not to commit Manifest.toml.
  • gpgsign::Bool: Whether or not to sign commits with your GPG key. This option requires that the Git CLI is installed, and for you to have a GPG key associated with your committer identity.
source

Continuous Integration (CI)

These plugins will create the configuration files of common CI services for you.

AppVeyor(;
+    file="~/build/invenia/PkgTemplates.jl/templates/appveyor.yml",
+    x86=false,
+    coverage=true,
+    extra_versions=["1.0", "1.0", "nightly"],
+)

Integrates your packages with AppVeyor via AppVeyor.jl.

Keyword Arguments

  • file::AbstractString: Template file for .appveyor.yml.
  • x86::Bool: Whether or not to run builds on 32-bit systems, in addition to the default 64-bit builds.
  • coverage::Bool: Whether or not to publish code coverage. Codecov must also be included.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.
source
CirrusCI(;
+    file="~/build/invenia/PkgTemplates.jl/templates/cirrus.yml",
+    image="freebsd-12-0-release-amd64",
+    coverage=true,
+    extra_versions=["1.0", "1.0", "nightly"],
+)

Integrates your packages with Cirrus CI via CirrusCI.jl.

Keyword Arguments

  • file::AbstractString: Template file for .cirrus.yml.
  • image::AbstractString: The FreeBSD image to be used.
  • coverage::Bool: Whether or not to publish code coverage. Codecov must also be included.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.
Note

Code coverage submission from Cirrus CI is not yet supported by Coverage.jl.

source
DroneCI(;
+    file="~/build/invenia/PkgTemplates.jl/templates/drone.star",
+    amd64=true,
+    arm=false,
+    arm64=false,
+    extra_versions=["1.0", "1.0"],
+)

Integrates your packages with Drone CI.

Keyword Arguments

  • file::AbstractString: Template file for .drone.star.
  • destination::AbstractString: File destination, relative to the repository root. For example, you might want to generate a .drone.yml instead of the default Starlark file.
  • amd64::Bool: Whether or not to run builds on AMD64.
  • arm::Bool: Whether or not to run builds on ARM (32-bit).
  • arm64::Bool: Whether or not to run builds on ARM64.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.
Note

Nightly Julia is not supported.

source
GitHubActions(;
+    file="~/build/invenia/PkgTemplates.jl/templates/github/workflows/ci.yml",
+    destination="ci.yml",
+    linux=true,
+    osx=true,
+    windows=true,
+    x64=true,
+    x86=false,
+    coverage=true,
+    extra_versions=["1.0", "1.0"],
+)

Integrates your packages with GitHub Actions.

Keyword Arguments

  • file::AbstractString: Template file for the workflow file.
  • destination::AbstractString: Destination of the worflow file, relative to .github/workflows.
  • linux::Bool: Whether or not to run builds on Linux.
  • osx::Bool: Whether or not to run builds on OSX (MacOS).
  • windows::Bool: Whether or not to run builds on Windows.
  • x64::Bool: Whether or not to run builds on 64-bit architecture.
  • x86::Bool: Whether or not to run builds on 32-bit architecture.
  • coverage::Bool: Whether or not to publish code coverage. Another code coverage plugin such as Codecov must also be included.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.
Note

If using coverage plugins, don't forget to manually add your API tokens as secrets, as described here.

Note

Nightly Julia is not supported.

source
GitLabCI(;
+    file="~/build/invenia/PkgTemplates.jl/templates/gitlab-ci.yml",
+    coverage=true,
+    extra_versions=["1.0", "1.0"],
+)

Integrates your packages with GitLab CI.

Keyword Arguments

  • file::AbstractString: Template file for .gitlab-ci.yml.
  • coverage::Bool: Whether or not to compute code coverage.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.

GitLab Pages

Documentation can be generated by including a Documenter{GitLabCI} plugin. See Documenter for more information.

Note

Nightly Julia is not supported.

source
TravisCI(;
+    file="~/build/invenia/PkgTemplates.jl/templates/travis.yml",
+    linux=true,
+    osx=true,
+    windows=true,
+    x64=true,
+    x86=false,
+    arm64=false,
+    coverage=true,
+    extra_versions=["1.0", "1.0", "nightly"],
+)

Integrates your packages with Travis CI.

Keyword Arguments

  • file::AbstractString: Template file for .travis.yml.
  • linux::Bool: Whether or not to run builds on Linux.
  • osx::Bool: Whether or not to run builds on OSX (MacOS).
  • windows::Bool: Whether or not to run builds on Windows.
  • x64::Bool: Whether or not to run builds on 64-bit architecture.
  • x86::Bool: Whether or not to run builds on 32-bit architecture.
  • arm64::Bool: Whether or not to run builds on the ARM64 architecture.
  • coverage::Bool: Whether or not to publish code coverage. Another code coverage plugin such as Codecov must also be included.
  • extra_versions::Vector: Extra Julia versions to test, as strings or VersionNumbers.
source

Code Coverage

These plugins will enable code coverage reporting from CI.

Codecov(; file=nothing)

Sets up code coverage submission from CI to Codecov.

Keyword Arguments

  • file::Union{AbstractString, Nothing}: Template file for .codecov.yml, or nothing to create no file.
source
Coveralls(; file=nothing)

Sets up code coverage submission from CI to Coveralls.

Keyword Arguments

  • file::Union{AbstractString, Nothing}: Template file for .coveralls.yml, or nothing to create no file.
source

Documentation

Documenter{T<:Union{TravisCI, GitLabCI, Nothing}}(;
+    make_jl="~/build/invenia/PkgTemplates.jl/templates/docs/make.jl",
+    index_md="~/build/invenia/PkgTemplates.jl/templates/docs/src/index.md",
+    assets=String[],
+    canonical_url=make_canonical(T),
+    makedocs_kwargs=Dict{Symbol, Any}(),
+)

Sets up documentation generation via Documenter.jl. Documentation deployment depends on T, where T is some supported CI plugin, or Nothing to only support local documentation builds.

Supported Type Parameters

Keyword Arguments

  • make_jl::AbstractString: Template file for make.jl.
  • index_md::AbstractString: Template file for index.md.
  • assets::Vector{<:AbstractString}: Extra assets for the generated site.
  • canonical_url::Union{Function, Nothing}: A function to generate the site's canonical URL. The default value will compute GitHub Pages and GitLab Pages URLs for TravisCI and GitLabCI, respectively. If set to nothing, no canonical URL is set.
  • makedocs_kwargs::Dict{Symbol}: Extra keyword arguments to be inserted into makedocs.
Note

If deploying documentation with Travis CI, don't forget to complete the required configuration.

source

Miscellaneous

Develop()

Adds generated packages to the current environment by deving them. See the Pkg documentation here for more details.

source
Citation(; file="~/build/invenia/PkgTemplates.jl/templates/CITATION.bib", readme=false)

Creates a CITATION.bib file for citing package repositories.

Keyword Arguments

  • file::AbstractString: Template file for CITATION.bib.
  • readme::Bool: Whether or not to include a section about citing in the README.
source

A More Complicated Example

Here are a few example templates that use the options and plugins explained above.

This one includes plugins suitable for a project hosted on GitHub, and some other customizations:

Template(; 
+    user="my-username",
+    dir="~/code",
+    authors="Acme Corp",
+    julia=v"1.1",
+    plugins=[
+        License(; name="MPL"),
+        Git(; manifest=true, ssh=true),
+        GitHubActions(; x86=true),
+        Codecov(),
+        Documenter{GitHubActions}(),
+        Develop(),
+    ],
+)

Here's one that works well for projects hosted on GitLab:

Template(;
+    user="my-username",
+    host="gitlab.com",
+    plugins=[
+        GitLabCI(),
+        Documenter{GitLabCI}(),
+    ],
+)

Custom Template Files

Templates vs Templating

This documentation refers plenty to Templates, the package's main type, but it also refers to "template files" and "text templating", which are plaintext files with placeholders to be filled with data, and the technique of filling those placeholders with data, respectively.

These concepts should be familiar if you've used Jinja or Mustache (Mustache is the particular flavour used by PkgTemplates, via Mustache.jl). Please keep the difference between these two things in mind!

Many plugins support a file argument or similar, which sets the path to the template file to be used for generating files. Each plugin has a sensible default that should make sense for most people, but you might have a specialized workflow that requires a totally different template file.

If that's the case, a basic understanding of Mustache's syntax is required. Here's an example template file:

Hello, {{{name}}}.
+
+{{#weather}}
+It's {{{weather}}} outside. 
+{{/weather}}
+{{^weather}}
+I don't know what the weather outside is.
+{{/weather}}
+
+{{#has_things}}
+I have the following things:
+{{/has_things}}
+{{#things}}
+- Here's a thing: {{{.}}}
+{{/things}}
+
+{{#people}}
+- {{{name}}} is {{{mood}}}
+{{/people}}

In the first section, name is a key, and its value replaces {{{name}}}.

In the second section, weather's value may or may not exist. If it does exist, then "It's weather outside" is printed. Otherwise, "I don't know what the weather outside is" is printed. Mustache uses a notion of "truthiness" similar to Python or JavaScript, where values of nothing, false, or empty collections are all considered to not exist.

In the third section, has_things' value is printed if it's truthy. Then, if the things list is truthy (i.e. not empty), its values are each printed on their own line. The reason that we have two separate keys is that {{#things}} iterates over the whole things list, even when there are no {{{.}}} placeholders, which would duplicate "I have the following things:" n times.

The fourth section iterates over the people list, but instead of using the {{{.}}} placeholder, we have name and mood, which are keys or fields of the list elements. Most types are supported here, including Dicts and structs. NamedTuples require you to use {{{:name}}} instead of the normal {{{name}}}, though.

You might notice that some curlies are in groups of two ({{key}}), and some are in groups of three ({{{key}}}). Whenever we want to subtitute in a value, using the triple curlies disables HTML escaping, which we rarely want for the types of files we're creating. If you do want escaping, just use the double curlies. And if you're using different delimiters, for example <<foo>>, use <<&foo>> to disable escaping.

Assuming the following view:

struct Person; name::String; mood::String; end
+things = ["a", "b", "c"]
+view = Dict(
+    "name" => "Chris",
+    "weather" => "sunny",
+    "has_things" => !isempty(things),
+    "things" => things,
+    "people" => [Person("John", "happy"), Person("Jane", "sad")],
+)

Our example template would produce this:

Hello, Chris.
+
+It's sunny outside.
+
+I have the following things:
+- Here's a thing: a
+- Here's a thing: b
+- Here's a thing: c
+
+- John is happy
+- Jane is sad

Extending Existing Plugins

Most of the existing plugins generate a file from a template file. If you want to use custom template files, you may run into situations where the data passed into the templating engine is not sufficient. In this case, you can look into implementing user_view to supply whatever data is necessary for your use case.

user_view(::Plugin, ::Template, pkg::AbstractString) -> Dict{String, Any}

The same as view, but for use by package users for extension.

Values returned by this function will override those from view when the keys are the same.

source

For example, suppose you were using the Readme plugin with a custom template file that looked like this:

# {{PKG}}
+
+Created on *{{TODAY}}*.

The view function supplies a value for PKG, but it does not supply a value for TODAY. Rather than override view, we can implement this function to get both the default values and whatever else we need to add.

user_view(::Readme, ::Template, ::AbstractString) = Dict("TODAY" => today())

Saving Templates

One of the main reasons for PkgTemplates' existence is for new packages to be consistent. This means using the same template more than once, so we want a way to save a template to be used later.

Here's my recommendation for loading a template whenever it's needed:

function template()
+    @eval using PkgTemplates
+    Template(; #= ... =#)
+end

Add this to your startup.jl, and you can create your template from anywhere, without incurring any startup cost.

Another strategy is to write the string representation of the template to a Julia file:

const t = Template(; #= ... =#)
+open("template.jl", "w") do io
+    println(io, "using PkgTemplates")
+    sprint(show, io, t)
+end

Then the template is just an include away:

const t = include("template.jl")

The only disadvantage to this approach is that the saved template is much less human-readable than code you wrote yourself.

One more method of saving templates is to simply use the Serialization package in the standard library:

const t = Template(; #= ... =#)
+using Serialization
+open(io -> serialize(io, t), "template.bin", "w")

Then simply deserialize to load:

using Serialization
+const t = open(deserialize, "template.bin")

This approach has the same disadvantage as the previous one, and the serialization format is not guaranteed to be stable across Julia versions.