PkgTemplates.jl/dev/developer/index.html
2020-05-15 16:37:06 +00:00

128 lines
44 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/index.html"/><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="#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="#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/6409473dbf515f3e24811aefa1532476c2fd366a/src/PkgTemplates.jl#LL36-L41">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL20-L22">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL161-L169">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL172-L182">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL185-L198">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL201-L206">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL85-L93">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">@with_kw_noshow 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>@with_kw_noshow</code> macro defines keyword constructors 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.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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL25-L30">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL96-L103">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL106-L113">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL134-L145">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, although that&#39;s less powerful.</p><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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL33-L45">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) -&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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL239-L245">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL250-L256">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL227-L232">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL58-L70">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL75-L82">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>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">@with_kw_noshow 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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL116-L122">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL125-L131">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">@with_kw_noshow 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="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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugin.jl#LL261-L267">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/6409473dbf515f3e24811aefa1532476c2fd366a/src/plugins/ci.jl#LL399-L404">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/6409473dbf515f3e24811aefa1532476c2fd366a/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/6409473dbf515f3e24811aefa1532476c2fd366a/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/6409473dbf515f3e24811aefa1532476c2fd366a/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/6409473dbf515f3e24811aefa1532476c2fd366a/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 15 May 2020 16:37">Friday 15 May 2020</span>. Using Julia version 1.4.1.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html>