PkgTemplates User Guide

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

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

Template

PkgTemplates.TemplateType
Template(; kwargs...)

A configuration used to generate packages.

Keyword Arguments

User Options

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

Package Options

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

Template Plugins

Interactive Mode

  • interactive::Bool=false: In addition to specifying the template options with keywords, you can also build up a template by following a set of prompts. To create a template interactively, set this keyword to true. See also the similar generate function.

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

julia> t = Template();

julia> t("PkgName")
source
PkgTemplates.generateFunction
generate([pkg::AbstractString]) -> Template

Shortcut for Template(; interactive=true)(pkg). If no package name is supplied, you will be prompted for one.

source

Plugins

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

Default Plugins

These plugins are included by default. They can be overridden by supplying another value, or disabled by negating the type (!Type), both as elements of the plugins keyword.

PkgTemplates.ProjectFileType
ProjectFile(; version=v"1.0.0-DEV")

Creates a Project.toml.

Keyword Arguments

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

Creates a module entrypoint.

Keyword Arguments

  • file::AbstractString: Template file for src/<module>.jl.
source
PkgTemplates.TestsType
Tests(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/test/runtests.jlt",
    project=false,
    aqua=false,
    aqua_kwargs=NamedTuple(),
    jet=false,
)

Sets up testing for packages.

Keyword Arguments

  • file::AbstractString: Template file for runtests.jl.
  • project::Bool: Whether or not to create a new project for tests (test/Project.toml). See the Pkg docs for more details.
  • aqua::Bool: Controls whether or not to add quality tests with Aqua.jl.
  • aqua_kwargs::NamedTuple: Which keyword arguments to supply to Aqua tests (many people use ambiguities=false for example)
  • jet::Bool: Controls whether or not to add a linting test with JET.jl (works best on type-stable code)
Note

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

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

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

Keyword Arguments

  • file::AbstractString: Template file for the README.
  • destination::AbstractString: File destination, relative to the repository root. For example, values of "README" or "README.rst" might be desired.
  • inline_badges::Bool: Whether or not to put the badges on the same line as the package name.
  • badge_order::Vector{typeof(Plugin)}: Plugins in the order their badges should appear.
  • badge_off::Vector{typeof(Plugin)}: Plugins which should not have their badges added.
source
PkgTemplates.LicenseType
License(; name="MIT", path=nothing, destination="LICENSE")

Creates a license file.

Keyword Arguments

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

Creates a Git repository and a .gitignore file.

Keyword Arguments

  • ignore::Vector{<:AbstractString}: Patterns to add to the .gitignore. See also: gitignore.
  • name::AbstractString: Your real name, if you have not set user.name with Git.
  • email::AbstractString: Your email address, if you have not set user.email with Git.
  • branch::AbstractString: The desired name of the repository's default branch.
  • ssh::Bool: Whether or not to use SSH for the remote. If left unset, HTTPS is used.
  • jl::Bool: Whether or not to add a .jl suffix to the remote URL.
  • manifest::Bool: Whether or not to commit Manifest.toml.
  • gpgsign::Bool: Whether or not to sign commits with your GPG key. This option requires that the Git CLI is installed, and for you to have a GPG key associated with your committer identity.
source
PkgTemplates.GitHubActionsType
GitHubActions(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/github/workflows/CI.yml",
    destination="CI.yml",
    linux=true,
    osx=false,
    windows=false,
    x64=true,
    x86=false,
    coverage=true,
    extra_versions=["1.6", "1.10", "pre"],
)

Integrates your packages with GitHub Actions.

Keyword Arguments

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

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

source
PkgTemplates.CompatHelperType
CompatHelper(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/github/workflows/CompatHelper.yml",
    destination="CompatHelper.yml",
    cron="0 0 * * *",
)

Integrates your packages with CompatHelper via GitHub Actions.

Keyword Arguments

  • file::AbstractString: Template file for the workflow file.
  • destination::AbstractString: Destination of the workflow file, relative to .github/workflows.
  • cron::AbstractString: Cron expression for the schedule interval.
source
PkgTemplates.TagBotType
TagBot(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/github/workflows/TagBot.yml",
    destination="TagBot.yml",
    trigger="JuliaTagBot",
    token=Secret("GITHUB_TOKEN"),
    ssh=Secret("DOCUMENTER_KEY"),
    ssh_password=nothing,
    changelog=nothing,
    changelog_ignore=nothing,
    gpg=nothing,
    gpg_password=nothing,
    registry=nothing,
    branches=nothing,
    dispatch=nothing,
    dispatch_delay=nothing,
)

Adds GitHub release support via TagBot.

Keyword Arguments

  • file::AbstractString: Template file for the workflow file.
  • destination::AbstractString: Destination of the workflow file, relative to .github/workflows.
  • trigger::AbstractString: Username of the trigger user for custom registries.
  • token::Secret: Name of the token secret to use.
  • ssh::Secret: Name of the SSH private key secret to use.
  • ssh_password::Secret: Name of the SSH key password secret to use.
  • changelog::AbstractString: Custom changelog template.
  • changelog_ignore::Vector{<:AbstractString}: Issue/pull request labels to ignore in the changelog.
  • gpg::Secret: Name of the GPG private key secret to use.
  • gpg_password::Secret: Name of the GPG private key password secret to use.
  • registry::AbstractString: Custom registry, in the format owner/repo.
  • branches::Bool: Whether not to enable the branches option.
  • dispatch::Bool: Whether or not to enable the dispatch option.
  • dispatch_delay::Int: Number of minutes to delay for dispatch events.
source
PkgTemplates.SecretType
Secret(name::AbstractString)

Represents a GitHub repository secret. When converted to a string, yields ${{ secrets.<name> }}.

source
PkgTemplates.DependabotType
Dependabot(; file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/github/dependabot.yml")

Setups Dependabot to create PRs whenever GitHub actions can be updated. This is very similar to CompatHelper, which performs the same task for Julia package dependencies.

Only for GitHub actions

Currently, this plugin is configured to setup Dependabot only for the GitHub actions package ecosystem. For example, it will create PRs whenever GitHub actions such as uses: actions/checkout@v3 can be updated to uses: actions/checkout@v4. If you want to configure Dependabot to update other package ecosystems, please modify the resulting file yourself.

Keyword Arguments

  • file::AbstractString: Template file for dependabot.yml.
source

Continuous Integration (CI)

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

PkgTemplates.AppVeyorType
AppVeyor(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/appveyor.yml",
    x86=false,
    coverage=true,
    extra_versions=["1.6", "1.10", "nightly"],
)

Integrates your packages with AppVeyor via AppVeyor.jl.

Keyword Arguments

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

Integrates your packages with Cirrus CI via CirrusCI.jl.

Keyword Arguments

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

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

source
PkgTemplates.DroneCIType
DroneCI(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/drone.star",
    amd64=true,
    arm=false,
    arm64=false,
    extra_versions=["1.6", "1.10"],
)

Integrates your packages with Drone CI.

Keyword Arguments

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

Nightly Julia is not supported.

source
PkgTemplates.GitLabCIType
GitLabCI(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/gitlab-ci.yml",
    coverage=true,
    extra_versions=["1.6", "1.10"],
)

Integrates your packages with GitLab CI.

Keyword Arguments

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

GitLab Pages

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

Note

Nightly Julia is not supported.

source
PkgTemplates.TravisCIType
TravisCI(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/travis.yml",
    linux=true,
    osx=false,
    windows=false,
    x64=true,
    x86=false,
    arm64=false,
    coverage=true,
    extra_versions=["1.6", "1.10", "nightly"],
)

Integrates your packages with Travis CI.

Keyword Arguments

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

Code Coverage

These plugins will enable code coverage reporting from CI.

PkgTemplates.CodecovType
Codecov(; file=nothing)

Sets up code coverage submission from CI to Codecov.

Keyword Arguments

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

Sets up code coverage submission from CI to Coveralls.

Keyword Arguments

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

Documentation

These plugins will help you build a documentation website.

PkgTemplates.DocumenterType
Documenter{T}(;
    make_jl="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/docs/make.jlt",
    index_md="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/docs/src/index.md",
    assets=String[],
    logo=Logo(),
    canonical_url=make_canonical(T),
    devbranch=nothing,
    edit_link=:devbranch,
    makedocs_kwargs=Dict{Symbol,Any}(),
)

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

Note

If you are deploying documentation with GitHub Actions or Travis CI, don't forget to complete the required configuration. In particular, you may need to run

using DocumenterTools; DocumenterTools.genkeys(user="MyUser", repo="MyPackage.jl")

and follow the instructions there.

Supported Type Parameters

Keyword Arguments

  • make_jl::AbstractString: Template file for make.jl.
  • index_md::AbstractString: Template file for index.md.
  • assets::Vector{<:AbstractString}: Extra assets for the generated site.
  • logo::Logo: A Logo containing documentation logo information.
  • canonical_url::Union{Function, Nothing}: A function to generate the site's canonical URL. The default value will compute GitHub Pages and GitLab Pages URLs for TravisCI and GitLabCI, respectively. If set to nothing, no canonical URL is set.
  • edit_link::Union{AbstractString, Symbol, Nothing}: Branch, tag or commit that the "Edit on…" link will point to. Defaults to the branch identified by devbranch. If edit_link=:commit, then the link will point to the latest commit when docs are built. If edit_link=nothing, then the "Edit on…" link will be hidden altogether.
  • devbranch::Union{AbstractString, Nothing}: Branch that will trigger docs deployment. If nothing, then the default branch according to the Template will be used.
  • makedocs_kwargs::Dict{Symbol,Any}: Extra keyword arguments to be inserted into makedocs.
source
Type
Logo(; light=nothing, dark=nothing)

Logo information for documentation.

Keyword Arguments

  • light::AbstractString: Path to a logo file for the light (default) theme.
  • dark::AbstractString: Path to a logo file for the dark theme.
source

Badges

These plugins will add badges to the README.

Miscellaneous

PkgTemplates.DevelopType
Develop()

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

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

Creates a CITATION.bib file for citing package repositories.

Keyword Arguments

  • file::AbstractString: Template file for CITATION.bib.
  • readme::Bool: Whether or not to include a section about citing in the README.
source
PkgTemplates.RegisterActionType
RegisterAction(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/github/workflows/Register.yml",
    destination="Register.yml",
    prompt="Version to register or component to bump",
)

Add a GitHub Actions workflow for registering a package with the General registry via workflow dispatch. See here for more information.

Keyword Arguments

  • file::AbstractString: Template file for the workflow file.
  • destination::AbstractString: Destination of the workflow file, relative to .github/workflows.
  • prompt::AbstractString: Prompt for workflow dispatch.
source
PkgTemplates.FormatterType
Formatter(;
    file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/.JuliaFormatter.toml",
    style="nostyle"
)

Create a .JuliaFormatter.toml file, used by JuliaFormatter.jl and the Julia VSCode extension to configure automatic code formatting.

This file can be entirely customized by the user, see the JuliaFormatter.jl docs.

Keyword Arguments

  • file::String: Template file for .JuliaFormatter.toml.
  • style::String: Style name, defaults to "nostyle" for an empty style but can also be one of ("sciml", "blue", "yas") for a fully preconfigured style.
source
PkgTemplates.CodeOwnersType
CodeOwners <: Plugin
CodeOwners(; owners)

A plugin which created GitLab/GitHub compatible CODEOWNERS files. owners should be a vector of patterns mapped to a vector of owner names. For example: owners=["*"=>["@invenia"], "README.md"=>["@documentation","@oxinabox]] assigns general ownership over all files to the invenia group, but assigns ownership of the readme to the documentation group and to the user oxinabox.

By default, it creates an empty CODEOWNERS file.

source
PkgTemplates.PkgBenchmarkType
PkgBenchmark(; file="~/work/PkgTemplates.jl/PkgTemplates.jl/templates/benchmark/benchmarks.jlt")

Sets up a PkgBenchmark.jl benchmark suite.

To ensure benchmark reproducibility, you will need to manually create an environment in the benchmark subfolder (for which the Manifest.toml is committed to version control). In this environment, you should at the very least:

  • pkg> add BenchmarkTools
  • pkg> dev your new package.

Keyword Arguments

  • file::AbstractString: Template file for benchmarks.jl.
source

A More Complicated Example

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

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

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

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

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

Custom Template Files

Templates vs Templating

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

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

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

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

Hello, {{{name}}}.

{{#weather}}
It's {{{weather}}} outside.
{{/weather}}
{{^weather}}
I don't know what the weather outside is.
{{/weather}}

{{#has_things}}
I have the following things:
{{/has_things}}
{{#things}}
- Here's a thing: {{{.}}}
{{/things}}

{{#people}}
- {{{name}}} is {{{mood}}}
{{/people}}

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

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

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

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

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

Assuming the following view:

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

Our example template would produce this:

Hello, Chris.

It's sunny outside.

I have the following things:
- Here's a thing: a
- Here's a thing: b
- Here's a thing: c

- John is happy
- Jane is sad

Extending Existing Plugins

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

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

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

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

source

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

# {{PKG}}

Created on *{{TODAY}}*.

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

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

Saving Templates

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

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

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

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

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

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

Then the template is just an include away:

const t = include("template.jl")

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

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

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

Then simply deserialize to load:

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

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