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.Template — Type
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. Likeuser, it takes its default value from the global Git config (user.nameanduser.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
plugins::Vector{<:Plugin}=Plugin[]: A list ofPlugins used by the template. The default plugins areProjectFile,SrcDir,Tests,Readme,License,Git,Dependabot,TagBotandGitHubActions. To disable a default plugin, pass in the negated type:!PluginType. To override a default plugin instead of disabling it, pass in your own instance.
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 totrue. See also the similargeneratefunction.
To create a package from a Template, use the following syntax:
julia> t = Template();
julia> t("PkgName")PkgTemplates.generate — Function
generate([pkg::AbstractString]) -> TemplateShortcut for Template(; interactive=true)(pkg). If no package name is supplied, you will be prompted for one.
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.ProjectFile — Type
ProjectFile(; version=v"1.0.0-DEV")Creates a Project.toml.
Keyword Arguments
version::VersionNumber: The initial version of created packages.
PkgTemplates.SrcDir — Type
SrcDir(; file="~/Developer/JuliaLang/Packages/PkgTemplates.jl/templates/src/module.jlt")Creates a module entrypoint.
Keyword Arguments
file::AbstractString: Template file forsrc/<module>.jl.
PkgTemplates.Tests — Type
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 forruntests.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 useambiguities=falsefor example)jet::Bool: Controls whether or not to add a linting test with JET.jl (works best on type-stable code)
PkgTemplates.Readme — Type
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 theREADME.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.
PkgTemplates.License — Type
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 overname.destination::AbstractString: File destination, relative to the repository root. For example,"LICENSE.md"might be desired.
PkgTemplates.Git — Type
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 setuser.namewith Git.email::AbstractString: Your email address, if you have not setuser.emailwith 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.jlsuffix to the remote URL.manifest::Bool: Whether or not to commitManifest.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.
PkgTemplates.GitHubActions — Type
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 asCodecovmust also be included.extra_versions::Vector: Extra Julia versions to test, as strings orVersionNumbers.
If using coverage plugins, don't forget to manually add your API tokens as secrets, as described here.
PkgTemplates.Dependabot — Type
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.
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 fordependabot.yml.
PkgTemplates.TagBot — Type
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 formatowner/repo.branches::Bool: Whether or not to enable thebranchesoption.dispatch::Bool: Whether or not to enable thedispatchoption.dispatch_delay::Int: Number of minutes to delay for dispatch events.
PkgTemplates.Secret — Type
Secret(name::AbstractString)Represents a GitHub repository secret. When converted to a string, yields ${{ secrets.<name> }}.
Continuous Integration (CI)
These plugins will create the configuration files of common CI services for you.
PkgTemplates.AppVeyor — Type
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.Codecovmust also be included.extra_versions::Vector: Extra Julia versions to test, as strings orVersionNumbers.
PkgTemplates.CirrusCI — Type
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.Codecovmust also be included.extra_versions::Vector: Extra Julia versions to test, as strings orVersionNumbers.
Code coverage submission from Cirrus CI is not yet supported by Coverage.jl.
PkgTemplates.DroneCI — Type
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.ymlinstead 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 orVersionNumbers.
PkgTemplates.GitLabCI — Type
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 orVersionNumbers.
GitLab Pages
Documentation can be generated by including a Documenter{GitLabCI} plugin. See Documenter for more information.
PkgTemplates.TravisCI — Type
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 asCodecovmust also be included.extra_versions::Vector: Extra Julia versions to test, as strings orVersionNumbers.
Code Coverage
These plugins will enable code coverage reporting from CI.
PkgTemplates.Codecov — Type
Codecov(; file=nothing)Sets up code coverage submission from CI to Codecov.
Keyword Arguments
file::Union{AbstractString, Nothing}: Template file for.codecov.yml, ornothingto create no file.
PkgTemplates.Coveralls — Type
Coveralls(; file=nothing)Sets up code coverage submission from CI to Coveralls.
Keyword Arguments
file::Union{AbstractString, Nothing}: Template file for.coveralls.yml, ornothingto create no file.
Documentation
These plugins will help you build a documentation website.
PkgTemplates.Documenter — Type
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.
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
GitHubActions: Deploys documentation to GitHub Pages with the help ofGitHubActions.TravisCI: Deploys documentation to GitHub Pages with the help ofTravisCI.GitLabCI: Deploys documentation to GitLab Pages with the help ofGitLabCI.NoDeploy(default): Does not set up documentation deployment.
Keyword Arguments
make_jl::AbstractString: Template file formake.jl.index_md::AbstractString: Template file forindex.md.assets::Vector{<:AbstractString}: Extra assets for the generated site.logo::Logo: ALogocontaining 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 forTravisCIandGitLabCI, respectively. If set tonothing, 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 bydevbranch. Ifedit_link=:commit, then the link will point to the latest commit when docs are built. Ifedit_link=nothing, then the "Edit on…" link will be hidden altogether.devbranch::Union{AbstractString, Nothing}: Branch that will trigger docs deployment. Ifnothing, then the default branch according to theTemplatewill be used.makedocs_kwargs::Dict{Symbol,Any}: Extra keyword arguments to be inserted intomakedocs.
PkgTemplates.Logo — 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.
Badges
These plugins will add badges to the README.
PkgTemplates.PkgEvalBadge — Type
PkgEvalBadge()Adds a PkgEval badge to the Readme file.
Miscellaneous
PkgTemplates.Develop — Type
Develop()Adds generated packages to the current environment by deving them. See the Pkg documentation here for more details.
PkgTemplates.Citation — Type
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 forCITATION.bib.readme::Bool: Whether or not to include a section about citing in the README.
PkgTemplates.RegisterAction — Type
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.
PkgTemplates.Formatter — Type
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.
PkgTemplates.CodeOwners — Type
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.
PkgTemplates.PkgBenchmark — Type
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 BenchmarkToolspkg> devyour new package.
Keyword Arguments
file::AbstractString: Template file forbenchmarks.jl.
PkgTemplates.CompatHelper — Type
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.
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.
PkgTemplates.Reuse — Type
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. Ifnothing, fall back to"MIT".artifact_license::Union{AbstractString, Nothing}: SPDX license expression for project artifacts and tooling files covered byREUSE.toml. Defaults tolicense.docs_license::Union{AbstractString, Nothing}: SPDX license expression for documentation text. Defaults tolicense.docs_assets_license::Union{AbstractString, Nothing}: SPDX license expression for documentation assets. Defaults todocs_license.license_ref_dir::Union{AbstractString, Nothing}: Optional directory containing user-supplied license texts forLicenseRef-...identifiers. ForLicenseRef-X,ReuseprefersLicenseRef-X.txt.mustache, rendered toLICENSES/LicenseRef-X.txt, and otherwise copiesLicenseRef-X.txtverbatim.template::AbstractString: Template file forREUSE.toml.enable_reuse_lint::Bool: Whether to addreuse lintto generated GitHub Actions workflows when theGitHubActionsplugin is present.readme_license_section::Bool: Whether to append a## Licensingsection to the README file generated by theReadmeplugin. If theReadmeplugin 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 rootLICENSEfile. If all effective license domains, after applying defaults, resolve to the same single plain license withoutWITHexceptions, that license text is copied fromLICENSES/to rootLICENSE. Otherwise, rootLICENSEis generated as a short pointer to the REUSE layout. Defaults totrue.license_approval::String: Controls optional checks against license approval metadata. The values"code","strict", and"none"may be given. Defaults to"code".
- Use SPDX license identifiers and expressions such as
MIT,Apache-2.0,GPL-3.0-or-later, orMIT OR Apache-2.0. - Deprecated GNU-style identifiers such as
GPL-3.0andGPL-3.0+are accepted as a convenience and normalized to modern SPDX identifiers such asGPL-3.0-onlyandGPL-3.0-or-later. - A trailing
+may be used for "or later" style expressions where applicable, e.g.,EUPL-1.2+. Reusewrites parsed and normalized SPDX expressions toREUSE.toml. License and exception texts copied toLICENSES/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 inlicense_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-Xpasses because the expression provides an OSI-approved path.WITHexceptions 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.0would fail.license_approval="none"disables OSI/FSF-libre approval checks and allows the broadest REUSE-compliant setup, including customLicenseRef-*licenses and license exceptions.
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.
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:
- The primary code licensing is taken from
Reuse.licensein the form of an SPDX license expression, if provided. Otherwise, the primary code license defaults to "MIT". - Separate license expressions may also be provided for project artifacts, documentation text, and documentation assets such as images.
- Parsed and normalized SPDX expressions are written to
REUSE.tomlin the package's root directory. Local SPDX headers may still override them on individual files. - Standard SPDX license and exception texts are copied into
LICENSES/from a bundled snapshot shipped withPkgTemplates. - 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 specifiedlicense_ref_dir. - If a
Licenseplugin has written a root license file andReuseis enabled, that file is removed to avoid ambiguity. Ifroot_license=true,Reusethen writes a REUSE-aware rootLICENSEfile. - Where needed, the plugin adds SPDX headers to generated files.
- If
GitHubActionsis present andenable_reuse_lint=true, the plugin adds a separatereuse lintjob to the generated GitHub Actions workflow. - Optionally, the plugin can also append a
## Licensingsection to the README file generated by theReadmeplugin. 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.
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.
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.
The plugin helps establish a REUSE-compatible project layout, but full compliance should still be verified with reuse lint.
Custom Template Files
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 sadExtending 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_view — Function
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.
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
endAdd 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)
endThen 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.