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="gwr-de": 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}}="Guido Wolf Reichert <gwr@bsl-support.de> 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.10.10": 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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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.10", "1.12", "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.DependabotType
Dependabot(; file="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/github/dependabot.yml")

Sets up Dependabot to create PRs whenever GitHub Actions or Julia package dependencies can be updated. Monitors the root /, /docs, and /test directories for Julia dependencies.

As of December 2025, Dependabot supports Julia and is the recommended approach for keeping package dependencies up to date. This replaces the functionality previously provided by CompatHelper.

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@v5 can be updated to uses: actions/checkout@v6. 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
PkgTemplates.TagBotType
TagBot(;
    file="~/Developer/JuliaLang/Packages/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 or 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

Continuous Integration (CI)

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

PkgTemplates.AppVeyorType
AppVeyor(;
    file="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/appveyor.yml",
    x86=false,
    coverage=true,
    extra_versions=["1.10", "1.12", "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="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/cirrus.yml",
    image="freebsd-12-0-release-amd64",
    coverage=true,
    extra_versions=["1.10", "1.12", "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="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/drone.star",
    amd64=true,
    arm=false,
    arm64=false,
    extra_versions=["1.10", "1.12"],
)

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="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/gitlab-ci.yml",
    coverage=true,
    extra_versions=["1.10", "1.12"],
)

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="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/travis.yml",
    linux=true,
    osx=false,
    windows=false,
    x64=true,
    x86=false,
    arm64=false,
    coverage=true,
    extra_versions=["1.10", "1.12", "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="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/docs/make.jlt",
    index_md="~/Developer/JuliaLang/Packages/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
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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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="~/Developer/JuliaLang/Packages/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
PkgTemplates.CompatHelperType
CompatHelper(;
    file="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/github/workflows/CompatHelper.yml",
    destination="CompatHelper.yml",
    cron="0 0 * * *",
)

Integrates your packages with CompatHelper via GitHub Actions.

Deprecated in favor of Dependabot

As of December 2025, Dependabot supports Julia and is now the recommended approach for keeping package dependencies up to date. The Dependabot plugin is included in the default template plugins. CompatHelper remains available for users who prefer it.

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.ReuseType
Reuse(;
    license=nothing,
    artifact_license=nothing,
    docs_license=nothing,
    docs_assets_license=nothing,
    license_ref_dir=nothing,
    template="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/reuse/REUSE.toml.mustache",
    enable_reuse_lint=true,
    readme_license_section=false,
    license_section_template="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/reuse/README_license_section.md.mustache",
    root_license=true,
    license_approval="code",
)

Set up a REUSE layout by generating REUSE.toml, copying the license and exception texts required by the configured SPDX license expressions into LICENSES/, and adding SPDX headers to generated source files.

SPDX-listed license and exception texts are taken exclusively from PkgTemplates’ bundled SPDX snapshot. Custom LicenseRef-... licenses are resolved from license_ref_dir. Only license texts required by the configured license expressions are written to LICENSES/.

Keyword Arguments

  • license::Union{AbstractString, Nothing}: Primary SPDX license expression for the project code. If nothing, fall back to "MIT".
  • artifact_license::Union{AbstractString, Nothing}: SPDX license expression for project artifacts and tooling files covered by REUSE.toml. Defaults to license.
  • docs_license::Union{AbstractString, Nothing}: SPDX license expression for documentation text. Defaults to license.
  • docs_assets_license::Union{AbstractString, Nothing}: SPDX license expression for documentation assets. Defaults to docs_license.
  • license_ref_dir::Union{AbstractString, Nothing}: Optional directory containing user-supplied license texts for LicenseRef-... identifiers. For LicenseRef-X, Reuse prefers LicenseRef-X.txt.mustache, rendered to LICENSES/LicenseRef-X.txt, and otherwise copies LicenseRef-X.txt verbatim.
  • template::AbstractString: Template file for REUSE.toml.
  • enable_reuse_lint::Bool: Whether to add reuse lint to generated GitHub Actions workflows when the GitHubActions plugin is present.
  • readme_license_section::Bool: Whether to append a ## Licensing section to the README file generated by the Readme plugin. If the Readme plugin is disabled or its destination file does not exist, no section is appended.
  • license_section_template::String: Template file for the optional README licensing section.
  • root_license::Bool: Whether to generate a conventional root LICENSE file. If all effective license domains, after applying defaults, resolve to the same single plain license without WITH exceptions, that license text is copied from LICENSES/ to root LICENSE. Otherwise, root LICENSE is generated as a short pointer to the REUSE layout. Defaults to true.
  • license_approval::String: Controls optional checks against license approval metadata. The values "code", "strict", and "none" may be given. Defaults to "code".

SPDX License Expressions

  • Use SPDX license identifiers and expressions such as MIT, Apache-2.0, GPL-3.0-or-later, or MIT OR Apache-2.0.
  • Deprecated GNU-style identifiers such as GPL-3.0 and GPL-3.0+ are accepted as a convenience and normalized to modern SPDX identifiers such as GPL-3.0-only and GPL-3.0-or-later.
  • A trailing + may be used for "or later" style expressions where applicable, e.g., EUPL-1.2+.
  • Reuse writes parsed and normalized SPDX expressions to REUSE.toml. License and exception texts copied to LICENSES/ are selected from the SPDX identifiers referenced by those expressions.
  • Custom license texts may be referenced with LicenseRef-... identifiers. The corresponding text must be provided in license_ref_dir.

License Approval Checking

license_approval must be one of the following strings: "code", "strict", "none". The behavior is:

  • license_approval="code" requires that the primary code license expression has an OSI-approved path according to the bundled SPDX License List metadata. For example, MIT OR LicenseRef-X passes because the expression provides an OSI-approved path. WITH exceptions are rejected here for now, as a conservative interpretation.
  • license_approval="strict" includes the requirements of "code" and additionally requires all other license expressions to have either an OSI-approved or FSF-libre path according to the bundled SPDX metadata. For example, CC-BY-NC-4.0 would fail.
  • license_approval="none" disables OSI/FSF-libre approval checks and allows the broadest REUSE-compliant setup, including custom LicenseRef-* licenses and license exceptions.
License Approval Interpretation

A failed approval check does not mean that a license is invalid, non-free, or unsuitable. It only means that the requested approval property cannot be verified from the bundled metadata.

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-2.0"),
        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}(),
    ],
)

REUSE-compliant Packages

REUSE is a convention for making copyright and licensing information for software projects explicit, machine-readable, and unambiguous. It combines canonical license texts in LICENSES/ with SPDX-based file-level licensing declarations, often combined with project-wide defaults in REUSE.toml, which can be overridden by local SPDX headers.

The Reuse plugin helps generate packages in a REUSE-compatible project layout.

Choosing an appropriate license is outside the scope of PkgTemplates; resources such as the REUSE FAQ, Interoperable Europe, and the SPDX License List can help identify suitable SPDX license identifiers.

Reuse vs License plugin

The License plugin is included by default, but it does not control Reuse license choices. When using Reuse, it is recommended to disable License with !License so the project uses the REUSE licensing layout exclusively. If the License plugin is not disabled, Reuse will take precedence and remove the license file written by License to avoid ambiguity.

Root LICENSE and approval checks

Julia General and several repository hosting services expect a conventional root LICENSE file. Therefore, Reuse defaults to root_license=true, while still generating the REUSE layout with REUSE.toml, SPDX headers, and canonical license texts in LICENSES/.

If all effective license domains, after applying defaults, resolve to the same single plain license without WITH exceptions, that license text is copied from LICENSES/ to root LICENSE. In all other cases, root LICENSE is generated as a short pointer to the REUSE layout. This avoids pretending that a multi-license project has one single root license text.

The license_approval option controls how strongly Reuse checks configured license expressions against OSI-approved and FSF-libre metadata from the bundled SPDX License List snapshot. The default, license_approval="code", checks the primary code license only. Use license_approval="strict" for stronger checks across all license domains, or license_approval="none" for the broadest REUSE/SPDX setup.

A failed approval check does not mean that a license is invalid, non-free, or unsuitable. It only means that the requested approval property cannot be verified from the bundled metadata.

These options only affect the initially generated layout. They do not guarantee that a package remains REUSE-compliant or General-compatible after generation; later edits can still change or break the licensing state.

Plugin behavior

The plugin behaves as follows:

  1. The primary code licensing is taken from Reuse.license in the form of an SPDX license expression, if provided. Otherwise, the primary code license defaults to "MIT".
  2. Separate license expressions may also be provided for project artifacts, documentation text, and documentation assets such as images.
  3. Parsed and normalized SPDX expressions are written to REUSE.toml in the package's root directory. Local SPDX headers may still override them on individual files.
  4. Standard SPDX license and exception texts are copied into LICENSES/ from a bundled snapshot shipped with PkgTemplates.
  5. Custom license texts may be referenced in license expressions using identifiers of the form LicenseRef-.... In that case, users must provide the corresponding text in the specified license_ref_dir.
  6. If a License plugin has written a root license file and Reuse is enabled, that file is removed to avoid ambiguity. If root_license=true, Reuse then writes a REUSE-aware root LICENSE file.
  7. Where needed, the plugin adds SPDX headers to generated files.
  8. If GitHubActions is present and enable_reuse_lint=true, the plugin adds a separate reuse lint job to the generated GitHub Actions workflow.
  9. Optionally, the plugin can also append a ## Licensing section to the README file generated by the Readme plugin. This section is intended as a convenience explanation of the project's REUSE-based licensing setup, and can be generated from the default template or from a user-supplied template file.
Generated documentation

The default REUSE.toml covers documentation source files, such as docs/src/**/*.md, rather than generated docs/build/** output. If generated documentation includes docstrings from source files, those docstrings follow the REUSE licensing information of their source files or snippets. A license statement rendered into the generated documentation can explain the result to readers, but it does not itself change the REUSE licensing information for the source files.

Examples

Hello REUSE

Template(;
    plugins=[
        !License,
        Reuse(),
    ],
)

This sets up the package with everything under the MIT license. A copy of that license text will be available as LICENSES/MIT.txt and, because this is a single plain-license setup, also as root LICENSE.

REUSE.toml is written and SPDX file headers are added to files in src/, test/, and benchmark/, for example. A ## Licensing section is added to README.md, naming MIT as the license for the code and pointing out that the project follows REUSE.

General registry ready

Template(;
    plugins=[
        !License,
        GitHubActions(),
        Reuse(;
            license = "EUPL-1.2+",
        ),
    ],
)

This is similar to the previous example, but here the primary code license is EUPL-1.2+, a valid SPDX 2.3 license expression using the + operator for “or later”. The generated project will contain LICENSES/EUPL-1.2.txt, and that text is copied to root LICENSE.

Additionally, reuse lint is added to the GitHub Actions workflow.

General registry compatibility

Julia General requires an OSI-approved software license located in the top-level directory of the package code, for example in a file named LICENSE or LICENSE.md. For packages intended for General, prefer a single plain OSI-approved primary code license, without OR, AND, WITH, or LicenseRef-* in the primary code license expression, and leave other license domains at their default nothing. This keeps the generated root LICENSE file as an actual license text rather than a REUSE pointer. The default root_license=true and license_approval="code" settings help with this path, but they do not by themselves guarantee General compatibility for every valid REUSE/SPDX setup.

Free-play

Template(;
    plugins=[
        !License,
        GitHubActions(),
        Reuse(;
            license = "GPL-3.0-or-later",
            artifact_license = "CC0-1.0",
            docs_license = "CC-BY-SA-4.0",
            license_approval = "strict",
        ),
    ],
)

This is similar to General registry ready, but now different license expressions are used. Code is licensed under GPL-3.0-or-later, artifacts under CC0-1.0, and documentation — including assets — under CC-BY-SA-4.0.

Since this is no longer a single-license setup, the root LICENSE file becomes a pointer to the REUSE layout instead of duplicating one license text. With license_approval="strict", non-code license domains are also checked against approval/libre metadata. For example, a non-commercial documentation license such as CC-BY-NC-4.0 would fail this check.

Maintaining and signaling compliance

Once the repository is publicly available and checked by the REUSE API, users can add a REUSE status badge to the README manually. For GitHub repositories, the badge URL has the form https://api.reuse.software/badge/github.com/USER/PKG.jl, linking to https://api.reuse.software/info/github.com/USER/PKG.jl.

Compliance check

The plugin helps establish a REUSE-compatible project layout, but full compliance should still be verified with reuse lint.

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.