PkgTemplates.jl/dev/developer/index.html
2020-06-12 00:08:20 +00:00

136 lines
52 KiB
HTML

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Developer Guide · PkgTemplates.jl</title><link rel="canonical" href="https://invenia.github.io/PkgTemplates.jl/developer/"/><link href="https://fonts.googleapis.com/css?family=Lato|Roboto+Mono" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/fontawesome.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/solid.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/brands.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.11.1/katex.min.css" rel="stylesheet" type="text/css"/><script>documenterBaseURL=".."</script><script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="../assets/documenter.js"></script><script src="../siteinfo.js"></script><script src="../../versions.js"></script><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../assets/themes/documenter-dark.css" data-theme-name="documenter-dark"/><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../assets/themes/documenter-light.css" data-theme-name="documenter-light" data-theme-primary/><script src="../assets/themeswap.js"></script></head><body><div id="documenter"><nav class="docs-sidebar"><div class="docs-package-name"><span class="docs-autofit">PkgTemplates.jl</span></div><form class="docs-search" action="../search/"><input class="docs-search-query" id="documenter-search-query" name="q" type="text" placeholder="Search docs"/></form><ul class="docs-menu"><li><a class="tocitem" href="../">Home</a></li><li><a class="tocitem" href="../user/">User Guide</a></li><li class="is-active"><a class="tocitem" href>Developer Guide</a><ul class="internal"><li><a class="tocitem" href="#Template-Package-Creation-Pipeline-1"><span>Template + Package Creation Pipeline</span></a></li><li><a class="tocitem" href="#Plugin-Walkthrough-1"><span><code>Plugin</code> Walkthrough</span></a></li><li><a class="tocitem" href="#FilePlugin-Walkthrough-1"><span><code>FilePlugin</code> Walkthrough</span></a></li><li><a class="tocitem" href="#Doing-Extra-Work-With-FilePlugins-1"><span>Doing Extra Work With <code>FilePlugin</code>s</span></a></li><li><a class="tocitem" href="#Supporting-Interactive-Mode-1"><span>Supporting Interactive Mode</span></a></li><li><a class="tocitem" href="#Miscellaneous-Tips-1"><span>Miscellaneous Tips</span></a></li><li><a class="tocitem" href="#Testing-1"><span>Testing</span></a></li></ul></li><li><a class="tocitem" href="../migrating/">Migrating To PkgTemplates 0.7+</a></li></ul><div class="docs-version-selector field has-addons"><div class="control"><span class="docs-label button is-static is-size-7">Version</span></div><div class="docs-selector control is-expanded"><div class="select is-fullwidth is-size-7"><select id="documenter-version-selector"></select></div></div></div></nav><div class="docs-main"><header class="docs-navbar"><nav class="breadcrumb"><ul class="is-hidden-mobile"><li class="is-active"><a href>Developer Guide</a></li></ul><ul class="is-hidden-tablet"><li class="is-active"><a href>Developer Guide</a></li></ul></nav><div class="docs-right"><a class="docs-edit-link" href="https://github.com/invenia/PkgTemplates.jl/blob/master/docs/src/developer.md#L" title="Edit on GitHub"><span class="docs-icon fab"></span><span class="docs-label is-hidden-touch">Edit on GitHub</span></a><a class="docs-settings-button fas fa-cog" id="documenter-settings-button" href="#" title="Settings"></a><a class="docs-sidebar-button fa fa-bars is-hidden-desktop" id="documenter-sidebar-button" href="#"></a></div></header><article class="content" id="documenter-page"><h1 id="PkgTemplates-Developer-Guide-1"><a class="docs-heading-anchor" href="#PkgTemplates-Developer-Guide-1">PkgTemplates Developer Guide</a><a class="docs-heading-anchor-permalink" href="#PkgTemplates-Developer-Guide-1" title="Permalink"></a></h1><ul><li><a href="#PkgTemplates-Developer-Guide-1">PkgTemplates Developer Guide</a></li><ul><li><a href="#Template-Package-Creation-Pipeline-1">Template + Package Creation Pipeline</a></li><li><a href="#Plugin-Walkthrough-1"><code>Plugin</code> Walkthrough</a></li><li><a href="#FilePlugin-Walkthrough-1"><code>FilePlugin</code> Walkthrough</a></li><li><a href="#Doing-Extra-Work-With-FilePlugins-1">Doing Extra Work With <code>FilePlugin</code>s</a></li><li><a href="#Supporting-Interactive-Mode-1">Supporting Interactive Mode</a></li><li><a href="#Miscellaneous-Tips-1">Miscellaneous Tips</a></li><li><a href="#Testing-1">Testing</a></li></ul></ul><p>PkgTemplates can be easily extended by adding new <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>s.</p><p>There are two types of plugins: <a href="#PkgTemplates.Plugin"><code>Plugin</code></a> and <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.Plugin" href="#PkgTemplates.Plugin"><code>PkgTemplates.Plugin</code></a><span class="docstring-category">Type</span></header><section><div><p>Plugins are PkgTemplates&#39; source of customization and extensibility. Add plugins to your <a href="../user/#PkgTemplates.Template"><code>Template</code></a>s to enable extra pieces of repository setup.</p><p>When implementing a new plugin, subtype this type to have full control over its behaviour.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/PkgTemplates.jl#LL39-L44">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.FilePlugin" href="#PkgTemplates.FilePlugin"><code>PkgTemplates.FilePlugin</code></a><span class="docstring-category">Type</span></header><section><div><p>A simple plugin that, in general, creates a single file.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL99-L101">source</a></section></article><h2 id="Template-Package-Creation-Pipeline-1"><a class="docs-heading-anchor" href="#Template-Package-Creation-Pipeline-1">Template + Package Creation Pipeline</a><a class="docs-heading-anchor-permalink" href="#Template-Package-Creation-Pipeline-1" title="Permalink"></a></h2><p>The <a href="../user/#PkgTemplates.Template"><code>Template</code></a> constructor basically does this:</p><pre><code class="language-none">- extract values from keyword arguments
- create a Template from the values
- for each plugin:
- validate plugin against the template</code></pre><p>The plugin validation step uses the <a href="#PkgTemplates.validate"><code>validate</code></a> function. It lets us catch mistakes before we try to generate packages.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.validate" href="#PkgTemplates.validate"><code>PkgTemplates.validate</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">validate(::Plugin, ::Template)</code></pre><p>Perform any required validation for a <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>.</p><p>It is preferred to do validation here instead of in <a href="#PkgTemplates.prehook"><code>prehook</code></a>, because this function is called at <a href="../user/#PkgTemplates.Template"><code>Template</code></a> construction time, whereas the prehook is only run at package generation time.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL240-L248">source</a></section></article><p>The package generation process looks like this:</p><pre><code class="language-none">- 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</code></pre><p>As you can tell, plugins play a central role in setting up a package.</p><p>The three main entrypoints for plugins to do work are the <a href="#PkgTemplates.prehook"><code>prehook</code></a>, the <a href="#PkgTemplates.hook"><code>hook</code></a>, and the <a href="#PkgTemplates.posthook"><code>posthook</code></a>. As the names might imply, they basically mean &quot;before the main stage&quot;, &quot;the main stage&quot;, and &quot;after the main stage&quot;, respectively.</p><p>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 <a href="../user/#PkgTemplates.Git"><code>Git</code></a> plugin uses <a href="#PkgTemplates.posthook"><code>posthook</code></a> to commit all generated files, but it wouldn&#39;t make sense to do that before the files are generated.</p><p>But what about dependencies within the same stage? In this case, we have <a href="#PkgTemplates.priority"><code>priority</code></a> to define which plugins go when. The <a href="../user/#PkgTemplates.Git"><code>Git</code></a> plugin also uses this function to lower its posthook&#39;s priority, so that even if other plugins generate files in their posthooks, they still get committed (provided that those plugins didn&#39;t set an even lower priority).</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.prehook" href="#PkgTemplates.prehook"><code>PkgTemplates.prehook</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">prehook(::Plugin, ::Template, pkg_dir::AbstractString)</code></pre><p>Stage 1 of the package generation process (the &quot;before&quot; stage, in general). At this point, <code>pkg_dir</code> is an empty directory that will eventually contain the package, and neither the <a href="#PkgTemplates.hook"><code>hook</code></a>s nor the <a href="#PkgTemplates.posthook"><code>posthook</code></a>s have run.</p><div class="admonition is-info"><header class="admonition-header">Note</header><div class="admonition-body"><p><code>pkg_dir</code> only stays empty until the first plugin chooses to create a file. See also: <a href="#PkgTemplates.priority"><code>priority</code></a>.</p></div></div></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL251-L261">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.hook" href="#PkgTemplates.hook"><code>PkgTemplates.hook</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">hook(::Plugin, ::Template, pkg_dir::AbstractString)</code></pre><p>Stage 2 of the package generation pipeline (the &quot;main&quot; stage, in general). At this point, the <a href="#PkgTemplates.prehook"><code>prehook</code></a>s have run, but not the <a href="#PkgTemplates.posthook"><code>posthook</code></a>s.</p><p><code>pkg_dir</code> is the directory in which the package is being generated (so <code>basename(pkg_dir)</code> is the package name).</p><div class="admonition is-info"><header class="admonition-header">Note</header><div class="admonition-body"><p>You usually shouldn&#39;t implement this function for <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s. If you do, it should probably <code>invoke</code> the generic method (otherwise, there&#39;s not much reason to subtype <code>FilePlugin</code>).</p></div></div></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL264-L277">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.posthook" href="#PkgTemplates.posthook"><code>PkgTemplates.posthook</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">posthook(::Plugin, ::Template, pkg_dir::AbstractString)</code></pre><p>Stage 3 of the package generation pipeline (the &quot;after&quot; stage, in general). At this point, both the <a href="#PkgTemplates.prehook"><code>prehook</code></a>s and <a href="#PkgTemplates.hook"><code>hook</code></a>s have run.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL280-L285">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.priority" href="#PkgTemplates.priority"><code>PkgTemplates.priority</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">priority(::Plugin, ::Union{typeof(prehook), typeof(hook), typeof(posthook)}) -&gt; Int</code></pre><p>Determines the order in which plugins are processed (higher goes first). The default priority (<code>DEFAULT_PRIORITY</code>), is <code>1000</code>.</p><p>You can implement this function per-stage (by using <code>::typeof(hook)</code>, for example), or for all stages by simply using <code>::Function</code>.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL164-L172">source</a></section></article><h2 id="Plugin-Walkthrough-1"><a class="docs-heading-anchor" href="#Plugin-Walkthrough-1"><code>Plugin</code> Walkthrough</a><a class="docs-heading-anchor-permalink" href="#Plugin-Walkthrough-1" title="Permalink"></a></h2><p>Concrete types that subtype <a href="#PkgTemplates.Plugin"><code>Plugin</code></a> directly are free to do almost anything. To understand how they&#39;re implemented, let&#39;s look at simplified versions of two plugins: <a href="../user/#PkgTemplates.Documenter"><code>Documenter</code></a> to explore templating, and <a href="../user/#PkgTemplates.Git"><code>Git</code></a> to further clarify the multi-stage pipeline.</p><h3 id="Example:-Documenter-1"><a class="docs-heading-anchor" href="#Example:-Documenter-1">Example: <code>Documenter</code></a><a class="docs-heading-anchor-permalink" href="#Example:-Documenter-1" title="Permalink"></a></h3><pre><code class="language-julia">@plugin struct Documenter &lt;: Plugin
make_jl::String = default_file(&quot;docs&quot;, &quot;make.jl&quot;)
index_md::String = default_file(&quot;docs&quot;, &quot;src&quot;, &quot;index.md&quot;)
end
gitignore(::Documenter) = [&quot;/docs/build/&quot;]
badges(::Documenter) = [
Badge(
&quot;Stable&quot;,
&quot;https://img.shields.io/badge/docs-stable-blue.svg&quot;,
&quot;https://{{{USER}}}.github.io/{{{PKG}}}.jl/stable&quot;,
),
Badge(
&quot;Dev&quot;,
&quot;https://img.shields.io/badge/docs-dev-blue.svg&quot;,
&quot;https://{{{USER}}}.github.io/{{{PKG}}}.jl/dev&quot;,
),
]
view(p::Documenter, t::Template, pkg::AbstractString) = Dict(
&quot;AUTHORS&quot; =&gt; join(t.authors, &quot;, &quot;),
&quot;PKG&quot; =&gt; pkg,
&quot;REPO&quot; =&gt; &quot;$(t.host)/$(t.user)/$pkg.jl&quot;,
&quot;USER&quot; =&gt; t.user,
)
function hook(p::Documenter, t::Template, pkg_dir::AbstractString)
pkg = basename(pkg_dir)
docs_dir = joinpath(pkg_dir, &quot;docs&quot;)
make = render_file(p.make_jl, combined_view(p, t, pkg), tags(p))
gen_file(joinpath(docs_dir, &quot;make.jl&quot;), make)
index = render_file(p.index_md, combined_view(p, t, pkg), tags(p))
gen_file(joinpath(docs_dir, &quot;src&quot;, &quot;index.md&quot;), index)
# What this function does is not relevant here.
create_documentation_project()
end</code></pre><p>The <code>@plugin</code> macro defines some helpful methods for us. Inside of our struct definition, we&#39;re using <a href="#PkgTemplates.default_file"><code>default_file</code></a> to refer to files in this repository.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.@plugin" href="#PkgTemplates.@plugin"><code>PkgTemplates.@plugin</code></a><span class="docstring-category">Macro</span></header><section><div><pre><code class="language-julia">@plugin struct ... end</code></pre><p>Define a plugin subtype with keyword constructors and default values.</p><p>For details on the general syntax, see <a href="https://mauro3.github.io/Parameters.jl/stable/manual/#Types-with-default-values-and-keyword-constructors-1">Parameters.jl</a>.</p><p>There are a few extra restrictions:</p><ul><li>Before using this macro, you must have imported <code>@with_kw_noshow</code> via <code>using PkgTemplates: @with_kw_noshow</code></li><li>The type must be a subtype of <a href="#PkgTemplates.Plugin"><code>Plugin</code></a> (or one of its abstract subtypes)</li><li>The type cannot be parametric</li><li>All fields must have default values</li></ul><p><strong>Example</strong></p><pre><code class="language-julia">using PkgTemplates: @plugin, @with_kw_noshow, Plugin
@plugin struct MyPlugin &lt;: Plugin
x::String = &quot;hello!&quot;
y::Union{Int, Nothing} = nothing
end</code></pre><p><strong>Implementing <code>@plugin</code> Manually</strong></p><p>If for whatever reason, you are unable to meet the criteria outlined above, you can manually implement the methods that <code>@plugin</code> would have created for you. This is only mandatory if you want to use your plugin in interactive mode.</p><p><strong>Keyword Constructors</strong></p><p>If possible, use <code>@with_kw_noshow</code> to create a keyword constructor for your type. Your type must be capable of being instantiated with no arguments.</p><p><strong>Default Values</strong></p><p>If your type&#39;s fields have sensible default values, implement <code>defaultkw</code> like so:</p><pre><code class="language-julia">using PkgTemplates: PkgTemplates, Plugin
struct MyPlugin &lt;: Plugin
x::String
end
PkgTemplates.defaultkw(::Type{MyPlugin}, ::Val{:x}) = &quot;my default&quot;</code></pre><p>Remember to add a method to the function belonging to PkgTemplates, rather than creating your own function that PkgTemplates won&#39;t see.</p><p>If your plugin&#39;s fields have no sane defaults, then you&#39;ll need to implement <a href="#PkgTemplates.prompt"><code>prompt</code></a> appropriately instead.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL4-L58">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.default_file" href="#PkgTemplates.default_file"><code>PkgTemplates.default_file</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">default_file(paths::AbstractString...) -&gt; String</code></pre><p>Return a path relative to the default template file directory (<code>~/build/invenia/PkgTemplates.jl/templates</code>).</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL104-L109">source</a></section></article><p>The first method we implement for <code>Documenter</code> is <a href="#PkgTemplates.gitignore"><code>gitignore</code></a>, so that packages created with this plugin ignore documentation build artifacts.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.gitignore" href="#PkgTemplates.gitignore"><code>PkgTemplates.gitignore</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">gitignore(::Plugin) -&gt; Vector{String}</code></pre><p>Return patterns that should be added to <code>.gitignore</code>. These are used by the <a href="../user/#PkgTemplates.Git"><code>Git</code></a> plugin.</p><p>By default, an empty list is returned.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL175-L182">source</a></section></article><p>Second, we implement <a href="#PkgTemplates.badges"><code>badges</code></a> to add a couple of badges to new packages&#39; README files.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.badges" href="#PkgTemplates.badges"><code>PkgTemplates.badges</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">badges(::Plugin) -&gt; Union{Badge, Vector{Badge}}</code></pre><p>Return a list of <a href="#PkgTemplates.Badge"><code>Badge</code></a>s, or just one, to be added to <code>README.md</code>. These are used by the <a href="../user/#PkgTemplates.Readme"><code>Readme</code></a> plugin to add badges to the README.</p><p>By default, an empty list is returned.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL185-L192">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.Badge" href="#PkgTemplates.Badge"><code>PkgTemplates.Badge</code></a><span class="docstring-category">Type</span></header><section><div><pre><code class="language-julia">Badge(hover::AbstractString, image::AbstractString, link::AbstractString)</code></pre><p>Container for Markdown badge data. Each argument can contain placeholders, which will be filled in with values from <a href="#PkgTemplates.combined_view"><code>combined_view</code></a>.</p><p><strong>Arguments</strong></p><ul><li><code>hover::AbstractString</code>: Text to appear when the mouse is hovered over the badge.</li><li><code>image::AbstractString</code>: URL to the image to display.</li><li><code>link::AbstractString</code>: URL to go to upon clicking the badge.</li></ul></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL213-L224">source</a></section></article><p>These two functions, <a href="#PkgTemplates.gitignore"><code>gitignore</code></a> and <a href="#PkgTemplates.badges"><code>badges</code></a>, are currently the only &quot;special&quot; functions for cross-plugin interactions. In other cases, you can still access the <a href="../user/#PkgTemplates.Template"><code>Template</code></a>&#39;s plugins to depend on the presence/properties of other plugins via <a href="#PkgTemplates.getplugin"><code>getplugin</code></a>, although that&#39;s less powerful.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.getplugin" href="#PkgTemplates.getplugin"><code>PkgTemplates.getplugin</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">getplugin(t::Template, ::Type{T&lt;:Plugin}) -&gt; Union{T, Nothing}</code></pre><p>Get the plugin of type <code>T</code> from the template <code>t</code>, if it&#39;s present.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/template.jl#LL156-L160">source</a></section></article><p>Third, we implement <a href="#PkgTemplates.view"><code>view</code></a>, which is used to fill placeholders in badges and rendered files.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.view" href="#PkgTemplates.view"><code>PkgTemplates.view</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">view(::Plugin, ::Template, pkg::AbstractString) -&gt; Dict{String, Any}</code></pre><p>Return the view to be passed to the text templating engine for this plugin. <code>pkg</code> is the name of the package being generated.</p><p>For <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, this is used for both the plugin badges (see <a href="#PkgTemplates.badges"><code>badges</code></a>) and the template file (see <a href="#PkgTemplates.source"><code>source</code></a>). For other <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>s, it is used only for badges, but you can always call it yourself as part of your <a href="#PkgTemplates.hook"><code>hook</code></a> implementation.</p><p>By default, an empty <code>Dict</code> is returned.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL112-L124">source</a></section></article><p>Finally, we implement <a href="#PkgTemplates.hook"><code>hook</code></a>, 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.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.render_file" href="#PkgTemplates.render_file"><code>PkgTemplates.render_file</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">render_file(file::AbstractString view::Dict{&lt;:AbstractString}, tags=nothing) -&gt; String</code></pre><p>Render a template file with the data in <code>view</code>. <code>tags</code> should be a tuple of two strings, which are the opening and closing delimiters, or <code>nothing</code> to use the default delimiters.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL318-L324">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.render_text" href="#PkgTemplates.render_text"><code>PkgTemplates.render_text</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">render_text(text::AbstractString, view::Dict{&lt;:AbstractString}, tags=nothing) -&gt; String</code></pre><p>Render some text with the data in <code>view</code>. <code>tags</code> should be a tuple of two strings, which are the opening and closing delimiters, or <code>nothing</code> to use the default delimiters.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL329-L335">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.gen_file" href="#PkgTemplates.gen_file"><code>PkgTemplates.gen_file</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">gen_file(file::AbstractString, text::AbstractString)</code></pre><p>Create a new file containing some given text. Trailing whitespace is removed, and the file will end with a newline.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL306-L311">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.combined_view" href="#PkgTemplates.combined_view"><code>PkgTemplates.combined_view</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">combined_view(::Plugin, ::Template, pkg::AbstractString) -&gt; Dict{String, Any}</code></pre><p>This function combines <a href="#PkgTemplates.view"><code>view</code></a> and <a href="../user/#PkgTemplates.user_view"><code>user_view</code></a> for use in text templating. If you&#39;re doing manual file creation or text templating (i.e. writing <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>s that are not <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s), then you should use this function rather than either of the former two.</p><div class="admonition is-info"><header class="admonition-header">Note</header><div class="admonition-body"><p>Do not implement this function yourself! If you&#39;re implementing a plugin, you should implement <a href="#PkgTemplates.view"><code>view</code></a>. If you&#39;re customizing a plugin as a user, you should implement <a href="../user/#PkgTemplates.user_view"><code>user_view</code></a>.</p></div></div></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL137-L149">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.tags" href="#PkgTemplates.tags"><code>PkgTemplates.tags</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">tags(::Plugin) -&gt; Tuple{String, String}</code></pre><p>Return the delimiters used for text templating. See the <a href="../user/#PkgTemplates.Citation"><code>Citation</code></a> plugin for a rare case where changing the tags is necessary.</p><p>By default, the tags are <code>&quot;{{&quot;</code> and <code>&quot;}}&quot;</code>.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL154-L161">source</a></section></article><p>For more information on text templating, see the <a href="#FilePlugin-Walkthrough-1"><code>FilePlugin</code> Walkthrough</a> and the section on <a href="../user/#Custom-Template-Files-1">Custom Template Files</a>.</p><h3 id="Example:-Git-1"><a class="docs-heading-anchor" href="#Example:-Git-1">Example: <code>Git</code></a><a class="docs-heading-anchor-permalink" href="#Example:-Git-1" title="Permalink"></a></h3><pre><code class="language-julia">struct Git &lt;: Plugin end
priority(::Git, ::typeof(posthook)) = 5
function validate(::Git, ::Template)
foreach((&quot;user.name&quot;, &quot;user.email&quot;)) do k
if isempty(LibGit2.getconfig(k, &quot;&quot;))
throw(ArgumentError(&quot;Git: Global Git config is missing required value &#39;$k&#39;&quot;))
end
end
end
function prehook(::Git, t::Template, pkg_dir::AbstractString)
LibGit2.with(LibGit2.init(pkg_dir)) do repo
LibGit2.commit(repo, &quot;Initial commit&quot;)
pkg = basename(pkg_dir)
url = &quot;https://$(t.host)/$(t.user)/$pkg.jl&quot;
close(GitRemote(repo, &quot;origin&quot;, 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, &quot;.gitignore&quot;), join(ignore, &quot;\n&quot;))
end
function posthook(::Git, ::Template, pkg_dir::AbstractString)
LibGit2.with(GitRepo(pkg_dir)) do repo
LibGit2.add!(repo, &quot;.&quot;)
LibGit2.commit(repo, &quot;Files generated by PkgTemplates&quot;)
end
end</code></pre><p>We didn&#39;t use <code>@plugin</code> for this one, because there are no fields. Validation and all three hooks are implemented:</p><ul><li><a href="#PkgTemplates.validate"><code>validate</code></a> makes sure that all required Git configuration is present.</li><li><a href="#PkgTemplates.prehook"><code>prehook</code></a> creates the Git repository for the package.</li><li><a href="#PkgTemplates.hook"><code>hook</code></a> generates the <code>.gitignore</code> file, using the special <a href="#PkgTemplates.gitignore"><code>gitignore</code></a> function.</li><li><a href="#PkgTemplates.posthook"><code>posthook</code></a> adds and commits all the generated files.</li></ul><p>As previously mentioned, we use <a href="#PkgTemplates.priority"><code>priority</code></a> to make sure that we wait until all other plugins are finished their work before committing files.</p><p>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!</p><h2 id="FilePlugin-Walkthrough-1"><a class="docs-heading-anchor" href="#FilePlugin-Walkthrough-1"><code>FilePlugin</code> Walkthrough</a><a class="docs-heading-anchor-permalink" href="#FilePlugin-Walkthrough-1" title="Permalink"></a></h2><p>Most of the time, you don&#39;t really need all of the control that we showed off above. Plugins that subtype <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a> perform a much more limited task. In general, they just generate one templated file.</p><p>To illustrate, let&#39;s look at the <a href="../user/#PkgTemplates.Citation"><code>Citation</code></a> plugin, which creates a <code>CITATION.bib</code> file.</p><pre><code class="language-julia">@plugin struct Citation &lt;: FilePlugin
file::String = default_file(&quot;CITATION.bib&quot;)
end
source(p::Citation) = p.file
destination(::Citation) = &quot;CITATION.bib&quot;
tags(::Citation) = &quot;&lt;&lt;&quot;, &quot;&gt;&gt;&quot;
view(::Citation, t::Template, pkg::AbstractString) = Dict(
&quot;AUTHORS&quot; =&gt; join(t.authors, &quot;, &quot;),
&quot;MONTH&quot; =&gt; month(today()),
&quot;PKG&quot; =&gt; pkg,
&quot;URL&quot; =&gt; &quot;https://$(t.host)/$(t.user)/$pkg.jl&quot;,
&quot;YEAR&quot; =&gt; year(today()),
)</code></pre><p>Similar to the <code>Documenter</code> example above, we&#39;re defining a keyword constructor, and assigning a default template file from this repository. This plugin adds nothing to <code>.gitignore</code>, and it doesn&#39;t add any badges, so implementations for <a href="#PkgTemplates.gitignore"><code>gitignore</code></a> and <a href="#PkgTemplates.badges"><code>badges</code></a> are omitted.</p><p>First, we implement <a href="#PkgTemplates.source"><code>source</code></a> and <a href="#PkgTemplates.destination"><code>destination</code></a> to define where the template file comes from, and where it goes. These functions are specific to <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, and have no effect on regular <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>s by default.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.source" href="#PkgTemplates.source"><code>PkgTemplates.source</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">source(::FilePlugin) -&gt; Union{String, Nothing}</code></pre><p>Return the path to a plugin&#39;s template file, or <code>nothing</code> to indicate no file.</p><p>By default, <code>nothing</code> is returned.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL195-L201">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.destination" href="#PkgTemplates.destination"><code>PkgTemplates.destination</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">destination(::FilePlugin) -&gt; String</code></pre><p>Return the destination, relative to the package root, of a plugin&#39;s configuration file.</p><p>This function <strong>must</strong> be implemented.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL204-L210">source</a></section></article><p>Next, we implement <a href="#PkgTemplates.tags"><code>tags</code></a>. We briefly saw this function earlier, but in this case it&#39;s necessary to change its behaviour from the default. To see why, it might help to see the template file in its entirety:</p><pre><code class="language-none">@misc{&lt;&lt;&amp;PKG&gt;&gt;.jl,
author = {&lt;&lt;&amp;AUTHORS&gt;&gt;},
title = {&lt;&lt;&amp;PKG&gt;&gt;.jl},
url = {&lt;&lt;&amp;URL&gt;&gt;},
version = {v0.1.0},
year = {&lt;&lt;&amp;YEAR&gt;&gt;},
month = {&lt;&lt;&amp;MONTH&gt;&gt;}
}</code></pre><p>Because the file contains its own <code>{}</code> delimiters, we need to use different ones for templating to work properly.</p><p>Finally, we implement <a href="#PkgTemplates.view"><code>view</code></a> to fill in the placeholders that we saw in the template file.</p><h2 id="Doing-Extra-Work-With-FilePlugins-1"><a class="docs-heading-anchor" href="#Doing-Extra-Work-With-FilePlugins-1">Doing Extra Work With <code>FilePlugin</code>s</a><a class="docs-heading-anchor-permalink" href="#Doing-Extra-Work-With-FilePlugins-1" title="Permalink"></a></h2><p>Notice that we didn&#39;t have to implement <a href="#PkgTemplates.hook"><code>hook</code></a> for our plugin. It&#39;s implemented for all <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, like so:</p><pre><code class="language-julia">function render_plugin(p::FilePlugin, t::Template, pkg::AbstractString)
return render_file(source(p), combined_view(p, t, pkg), tags(p))
end
function hook(p::FilePlugin, t::Template, pkg_dir::AbstractString)
source(p) === nothing &amp;&amp; return
pkg = basename(pkg_dir)
path = joinpath(pkg_dir, destination(p))
text = render_plugin(p, t, pkg)
gen_file(path, text)
end</code></pre><p>But what if we want to do a little more than just generate one file?</p><p>A good example of this is the <a href="../user/#PkgTemplates.Tests"><code>Tests</code></a> plugin. It creates <code>runtests.jl</code>, but it also modifies the <code>Project.toml</code> to include the <code>Test</code> dependency.</p><p>Of course, we could use a normal <a href="#PkgTemplates.Plugin"><code>Plugin</code></a>, but it turns out there&#39;s a way to avoid that while still getting the extra capbilities that we want.</p><p>The plugin implements its own <code>hook</code>, but uses <code>invoke</code> to avoid duplicating the file creation code:</p><pre><code class="language-julia">@plugin struct Tests &lt;: FilePlugin
file::String = default_file(&quot;runtests.jl&quot;)
end
source(p::Tests) = p.file
destination(::Tests) = joinpath(&quot;test&quot;, &quot;runtests.jl&quot;)
view(::Tests, ::Template, pkg::AbstractString) = Dict(&quot;PKG&quot; =&gt; pkg)
function hook(p::Tests, t::Template, pkg_dir::AbstractString)
# Do the normal FilePlugin behaviour to create the test script.
invoke(hook, Tuple{FilePlugin, Template, AbstractString}, p, t, pkg_dir)
# Do some other work.
add_test_dependency()
end</code></pre><p>There is also a default <a href="#PkgTemplates.validate"><code>validate</code></a> implementation for <a href="#PkgTemplates.FilePlugin"><code>FilePlugin</code></a>s, which checks that the plugin&#39;s <a href="#PkgTemplates.source"><code>source</code></a> file exists, and throws an <code>ArgumentError</code> otherwise. If you want to extend the validation but keep the file existence check, use the <code>invoke</code> method as described above.</p><p>For more examples, see the plugins in the <a href="../user/#Continuous-Integration-(CI)-1">Continuous Integration (CI)</a> and <a href="../user/#Code-Coverage-1">Code Coverage</a> sections.</p><h2 id="Supporting-Interactive-Mode-1"><a class="docs-heading-anchor" href="#Supporting-Interactive-Mode-1">Supporting Interactive Mode</a><a class="docs-heading-anchor-permalink" href="#Supporting-Interactive-Mode-1" title="Permalink"></a></h2><p>When it comes to supporting interactive mode for your custom plugins, you have two options: write your own <a href="#PkgTemplates.interactive"><code>interactive</code></a> method, or use the default one. If you choose the first option, then you are free to implement the method however you want. If you want to use the default implementation, then there are a few functions that you should be aware of, although in many cases you will not need to add any new methods.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.interactive" href="#PkgTemplates.interactive"><code>PkgTemplates.interactive</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">interactive(T::Type{&lt;:Plugin}) -&gt; T</code></pre><p>Interactively create a plugin of type <code>T</code>. Implement this method and ignore other related functions only if you want completely custom behaviour.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/interactive.jl#LL13-L18">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.prompt" href="#PkgTemplates.prompt"><code>PkgTemplates.prompt</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">prompt(::Type{P}, ::Type{T}, ::Val{name::Symbol}) -&gt; Any</code></pre><p>Prompts for an input of type <code>T</code> for field <code>name</code> of plugin type <code>P</code>. Implement this method to customize particular fields of particular types.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/interactive.jl#LL112-L117">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.customizable" href="#PkgTemplates.customizable"><code>PkgTemplates.customizable</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">customizable(::Type{&lt;:Plugin}) -&gt; Vector{Pair{Symbol, DataType}}</code></pre><p>Return a list of keyword arguments that the given plugin type accepts, which are not fields of the type, and should be customizable in interactive mode. For example, for a constructor <code>Foo(; x::Bool)</code>, provide <code>[x =&gt; Bool]</code>. If <code>T</code> has fields which should not be customizable, use <code>NotCustomizable</code> as the type.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/interactive.jl#LL48-L55">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.input_tips" href="#PkgTemplates.input_tips"><code>PkgTemplates.input_tips</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">input_tips(::Type{T}) -&gt; Vector{String}</code></pre><p>Provide some extra tips to users on how to structure their input for the type <code>T</code>, for example if multiple delimited values are expected.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/interactive.jl#LL66-L71">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.convert_input" href="#PkgTemplates.convert_input"><code>PkgTemplates.convert_input</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">convert_input(::Type{P}, ::Type{T}, s::AbstractString) -&gt; T</code></pre><p>Convert the user input <code>s</code> into an instance of <code>T</code> for plugin of type <code>P</code>. A default implementation of <code>T(s)</code> exists.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/interactive.jl#LL78-L83">source</a></section></article><h2 id="Miscellaneous-Tips-1"><a class="docs-heading-anchor" href="#Miscellaneous-Tips-1">Miscellaneous Tips</a><a class="docs-heading-anchor-permalink" href="#Miscellaneous-Tips-1" title="Permalink"></a></h2><h3 id="Writing-Template-Files-1"><a class="docs-heading-anchor" href="#Writing-Template-Files-1">Writing Template Files</a><a class="docs-heading-anchor-permalink" href="#Writing-Template-Files-1" title="Permalink"></a></h3><p>For an overview of writing template files for Mustache.jl, see <a href="../user/#Custom-Template-Files-1">Custom Template Files</a> in the user guide.</p><h3 id="Predicates-1"><a class="docs-heading-anchor" href="#Predicates-1">Predicates</a><a class="docs-heading-anchor-permalink" href="#Predicates-1" title="Permalink"></a></h3><p>There are a few predicate functions for plugins that are occasionally used to answer questions like &quot;does this <code>Template</code> have any code coverage plugins?&quot;. If you&#39;re implementing a plugin that fits into one of the following categories, it would be wise to implement the corresponding predicate function to return <code>true</code> for instances of your type.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.needs_username" href="#PkgTemplates.needs_username"><code>PkgTemplates.needs_username</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">needs_username(::Plugin) -&gt; Bool</code></pre><p>Determine whether or not a plugin needs a Git hosting service username to function correctly. If you are implementing a plugin that uses the <code>user</code> field of a <a href="../user/#PkgTemplates.Template"><code>Template</code></a>, you should implement this function and return <code>true</code>.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugin.jl#LL340-L346">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.is_ci" href="#PkgTemplates.is_ci"><code>PkgTemplates.is_ci</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">is_ci(::Plugin) -&gt; Bool</code></pre><p>Determine whether or not a plugin is a CI plugin. If you are adding a CI plugin, you should implement this function and return <code>true</code>.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugins/ci.jl#LL411-L416">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.is_coverage" href="#PkgTemplates.is_coverage"><code>PkgTemplates.is_coverage</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">is_coverage(::Plugin) -&gt; Bool</code></pre><p>Determine whether or not a plugin is a coverage plugin. If you are adding a coverage plugin, you should implement this function and return <code>true</code>.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugins/coverage.jl#LL53-L58">source</a></section></article><h3 id="Formatting-Version-Numbers-1"><a class="docs-heading-anchor" href="#Formatting-Version-Numbers-1">Formatting Version Numbers</a><a class="docs-heading-anchor-permalink" href="#Formatting-Version-Numbers-1" title="Permalink"></a></h3><p>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.</p><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.compat_version" href="#PkgTemplates.compat_version"><code>PkgTemplates.compat_version</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">compat_version(v::VersionNumber) -&gt; String</code></pre><p>Format a <code>VersionNumber</code> to exclude trailing zero components.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugins/project_file.jl#LL27-L31">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.format_version" href="#PkgTemplates.format_version"><code>PkgTemplates.format_version</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">format_version(v::Union{VersionNumber, AbstractString}) -&gt; String</code></pre><p>Strip everything but the major and minor release from a <code>VersionNumber</code>. Strings are left in their original form.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugins/ci.jl#LL1-L6">source</a></section></article><article class="docstring"><header><a class="docstring-binding" id="PkgTemplates.collect_versions" href="#PkgTemplates.collect_versions"><code>PkgTemplates.collect_versions</code></a><span class="docstring-category">Function</span></header><section><div><pre><code class="language-julia">collect_versions(t::Template, versions::Vector) -&gt; Vector{String}</code></pre><p>Combine <code>t</code>&#39;s Julia version with <code>versions</code>, and format them as <code>major.minor</code>. This is useful for creating lists of versions to be included in CI configurations.</p></div><a class="docs-sourcelink" target="_blank" href="https://github.com/invenia/PkgTemplates.jl/blob/f61215823a3b407f1d3b81bd5059edab6bf3e290/src/plugins/ci.jl#LL388-L393">source</a></section></article><h2 id="Testing-1"><a class="docs-heading-anchor" href="#Testing-1">Testing</a><a class="docs-heading-anchor-permalink" href="#Testing-1" title="Permalink"></a></h2><p>If you write a cool new plugin that could be useful to other people, or find and fix a bug, you&#39;re encouraged to open a pull request with your changes. Here are some testing tips to ensure that your PR goes through as smoothly as possible.</p><h3 id="Updating-Reference-Tests-and-Fixtures-1"><a class="docs-heading-anchor" href="#Updating-Reference-Tests-and-Fixtures-1">Updating Reference Tests &amp; Fixtures</a><a class="docs-heading-anchor-permalink" href="#Updating-Reference-Tests-and-Fixtures-1" title="Permalink"></a></h3><p>If you&#39;ve added or modified plugins, you should update the reference tests and the associated test fixtures. In <code>test/reference.jl</code>, you&#39;ll find a &quot;Reference tests&quot; test set that basically generates a bunch of packages, and then checks each file against a reference file, which is stored somewhere in <code>test/fixtures</code>.</p><p>For new plugins, you should add an instance of your plugin to the &quot;All plugins&quot; anad &quot;Wacky options&quot; test sets, then run the tests with <code>Pkg.test</code>. They should pass, and there will be new files in <code>test/fixtures</code>. Check them to make sure that they contain exactly what you would expect!</p><p>For changes to existing plugins, update the plugin options appropriately in the &quot;Wacky options&quot; test set. Failing tests will give you the option to review and accept changes to the fixtures, updating the files automatically for you.</p><h3 id="Updating-&quot;Show&quot;-Tests-1"><a class="docs-heading-anchor" href="#Updating-&quot;Show&quot;-Tests-1">Updating &quot;Show&quot; Tests</a><a class="docs-heading-anchor-permalink" href="#Updating-&quot;Show&quot;-Tests-1" title="Permalink"></a></h3><p>Depending on what you&#39;ve changed, the tests in <code>test/show.jl</code> might fail. To fix those, you&#39;ll need to update the <code>expected</code> value to match what is actually displayed in a Julia REPL (assuming that the new value is correct).</p></article><nav class="docs-footer"><a class="docs-footer-prevpage" href="../user/">« User Guide</a><a class="docs-footer-nextpage" href="../migrating/">Migrating To PkgTemplates 0.7+ »</a></nav></div><div class="modal" id="documenter-settings"><div class="modal-background"></div><div class="modal-card"><header class="modal-card-head"><p class="modal-card-title">Settings</p><button class="delete"></button></header><section class="modal-card-body"><p><label class="label">Theme</label><div class="select"><select id="documenter-themepicker"><option value="documenter-light">documenter-light</option><option value="documenter-dark">documenter-dark</option></select></div></p><hr/><p>This document was generated with <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> on <span class="colophon-date" title="Friday 12 June 2020 00:08">Friday 12 June 2020</span>. Using Julia version 1.4.2.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html>