Python Semantic Release¶
Automatic Semantic Versioning for Python projects. This is a Python implementation of semantic-release for JS by Stephan Bönnemann. If you find this topic interesting you should check out his talk from JSConf Budapest.
The general idea is to be able to detect what the next version of the project should be based on the commits. This tool will use that to automate the whole release, upload to an artifact repository and post changelogs to GitHub. You can run the tool on a CI service, or just run it locally.
Installation¶
python3 -m pip install python-semantic-release
semantic-release --help
Python Semantic Release is also available from conda-forge or as a GitHub Action. Read more about the setup and configuration in our getting started guide.
Documentation Contents¶
Commands¶
All commands accept a -h/--help
option, which displays the help text for the
command and exits immediately.
semantic-release
does not allow interspersed arguments and options, which
means that the options for semantic-release
are not necessarily accepted
one of the subcommands. In particular, the --noop and
-v/--verbose flags must be given to the top-level
semantic-release
command, before the name of the subcommand.
For example:
Incorrect:
semantic-release version --print --noop -vv
Correct:
semantic-release -vv --noop version --print
With the exception of semantic-release and semantic-release generate-config, all commands require that you have set up your project’s configuration. To help with this step, semantic-release generate-config can create the default configuration for you, which will allow you to tweak it to your needs rather than write it from scratch.
semantic-release
¶
Options:¶
--version
¶
Display the version of Python Semantic Release and exit
--noop
¶
Use this flag to see what semantic-release
intends to do without making changes
to your project. When using this option, semantic-release
can be run as many times
as you wish without any side-effects.
-v/--verbose
¶
Can be supplied more than once. Controls the verbosity of semantic-releases
logging
output (default level is WARNING
, use -v
for INFO
and -vv
for DEBUG
).
-c/--config [FILE]
¶
Specify the configuration file which Python Semantic Release should use. This can be any of the supported formats valid for -f/--format [FORMAT]
Default: pyproject.toml
See also
--strict
¶
Enable Strict Mode. This will cause a number of conditions to produce a non-zero exit code when passed, where they would otherwise have produced an exit code of 0. Enabling this allows, for example, certain conditions to cause failure of a CI pipeline, while omitting this flag would allow the pipeline to continue to run.
See also
semantic-release version
¶
Detect the semantically correct next version that should be applied to your project.
By default:
Write this new version to the project metadata locations specified in the configuration file
Build the project using build_command, if specified
Create a new commit with these locations and any other assets configured to be included in a release
Tag this commit according the configured format, with a tag that uniquely identifies the version being released
Push the new tag and commit to the remote for the repository
Create a release (if supported) in the remote VCS for this tag
Changelog generation is done identically to the way it is done in semantic-release changelog, but this command additionally ensures the updated changelog is included in the release commit that is made.
See also
Options:¶
--print
¶
Print the next version that will be applied, respecting the other command line options that are supplied, and exit. This flag is useful if you just want to see what the next version will be. Note that instead of printing nothing at all, if no release will be made, the current version is printed.
For example, you can experiment with which versions would be applied using the other command line options:
semantic-release version --print
semantic-release version --patch --print
semantic-release version --prerelease --print
--print-tag
¶
Same as the --print flag but prints the complete tag
name (ex. v1.0.0
or py-v1.0.0
) instead of the raw version number
(1.0.0
).
--print-last-released
¶
Print the last released version based on the Git tags. This flag is useful if you just want to see the released version without determining what the next version will be. Note if the version can not be found nothing will be printed.
--print-last-released-tag
¶
Same as the --print-last-released flag but prints the
complete tag name (ex. v1.0.0
or py-v1.0.0
) instead of the raw version
number (1.0.0
).
--major/--minor/--patch/--prerelease
¶
Force the next version to increment the major, minor or patch digits, or the prerelease revision, respectively. These flags are optional but mutually exclusive, so only one may be supplied, or none at all. Using these flags overrides the usual calculation for the next version; this can be useful, say, when a project wants to release its initial 1.0.0 version.
Warning
Using these flags will override the configured value of prerelease
(configured
in your Release Group),
regardless of your configuration or the current version.
To produce a prerelease with the appropriate digit incremented you should also supply the --as-prerelease flag. If you do not, using these flags will force a full (non-prerelease) version to be created.
For example, suppose your project’s current version is 0.2.1-rc.1
. The following
shows how these options can be combined with --as-prerelease
to force different
versions:
semantic-release version --prerelease --print
# 0.2.1-rc.2
semantic-release version --patch --print
# 0.2.2
semantic-release version --minor --print
# 0.3.0
semantic-release version --major --print
# 1.0.0
semantic-release version --minor --as-prerelease --print
# 0.3.0-rc.1
semantic-release version --prerelease --as-prerelease --print
# 0.2.1-rc.2
These options are forceful overrides, but there is no action required for subsequent releases performed using the usual calculation algorithm.
Supplying --prerelease
will cause Python Semantic Release to scan your project history
for any previous prereleases with the same major, minor and patch versions as the latest
version and the same prerelease token as the
one passed by command-line or configuration. If one is not found, --prerelease
will
produce the next version according to the following format:
f"{latest_version.major}.{latest_version.minor}.{latest_version.patch}-{prerelease_token}.1"
However, if Python Semantic Release identifies a previous prerelease version with the same major, minor and patch digits as the latest version, and the same prerelease token as the one supplied by command-line or configuration, then Python Semantic Release will increment the revision found on that previous prerelease version in its new version.
For example, if "0.2.1-rc.1"
and already exists as a previous version, and the latest version
is "0.2.1"
, invoking the following command will produce "0.2.1-rc.2"
:
semantic-release version --prerelease --prerelease-token "rc" --print
Warning
This is true irrespective of the branch from which "0.2.1-rc.1"
was released from.
The check for previous prereleases “leading up to” this normal version is intended to
help prevent collisions in git tags to an extent, but isn’t foolproof. As the example
shows it is possible to release a prerelease for a normal version that’s already been
released when using this flag, which would in turn be ignored by tools selecting
versions by SemVer precedence rules.
See also
--as-prerelease
¶
After performing the normal calculation of the next version, convert the resulting next version
to a prerelease before applying it. As with --major/--minor/--patch/--prerelease, this option
is a forceful override, but no action is required to resume calculating versions as normal on the
subsequent releases. The main distinction between --prerelease
and --as-prerelease
is that
the latter will not force a new version if one would not have been released without supplying
the flag.
This can be useful when making a single prerelease on a branch that would typically release normal versions.
If not specified in --prerelease-token [VALUE], the prerelease token is idenitified using the Multibranch Release Configuration
See the examples alongside --major/--minor/--patch/--prerelease for how to use this flag.
--prerelease-token [VALUE]
¶
Force the next version to use the value as the prerelease token. This overrides the configured value if one is present. If not used during a release producing a prerelease version, this option has no effect.
--build-metadata [VALUE]
¶
If given, append the value to the newly calculated version. This can be used, for example, to attach a run number from a CI service or a date to the version and tag that are created.
This value can also be set using the environment variable PSR_BUILD_METADATA
For example, assuming a project is currently at version 1.2.3:
$ semantic-release version --minor --print
1.3.0
$ semantic-release version --minor --print --build-metadata "run.12345"
1.3.0+run.12345
--commit/--no-commit
¶
Whether or not to perform a git commit
on modifications to source files made by semantic-release
during this
command invocation, and to run git tag
on this new commit with a tag corresponding to the new version.
If --no-commit
is supplied, it may disable other options derivatively; please see below.
Default: --commit
See also
--tag/--no-tag
¶
Whether or not to perform a git tag
to apply a tag of the corresponding to the new version during this
command invocation. This option manages the tag application separate from the commit handled by the --commit
option.
If --no-tag
is supplied, it may disable other options derivatively; please see below.
Default: --tag
--changelog/--no-changelog
¶
Whether or not to update the changelog file with changes introduced as part of the new version released.
Default: --changelog
See also
--push/--no-push
¶
Whether or not to push new commits and/or tags to the remote repository.
Default: --no-push
if –no-commit and
–no-tag is also supplied, otherwise push
is the default.
--vcs-release/--no-vcs-release
¶
Whether or not to create a “release” in the remote VCS service, if supported. Currently releases in GitHub and Gitea remotes are supported. If releases aren’t supported in a remote VCS, this option will not cause a command failure, but will produce a warning.
Default: --no-vcs-release
if --no-push
is supplied (including where this is
implied by supplying only --no-commit
), otherwise --vcs-release
--skip-build
¶
If passed, skip building the current project using build_command.
semantic-release publish
¶
Publish a distribution to a VCS release. Uploads using publish
See also
Options:¶
--tag
¶
The tag associated with the release to publish to. If not given or set to “latest”, then Python Semantic Release will examine the Git tags in your repository to identify the latest version, and attempt to publish to a Release corresponding to this version.
Default: “latest”
semantic-release generate-config
¶
Generate default configuration for semantic-release, to help you get started quickly. You can inspect the defaults, write to a file and then edit according to your needs. For example, to append the default configuration to your pyproject.toml file, you can use the following command:
$ semantic-release generate-config -f toml --pyproject >> pyproject.toml
If your project doesn’t already leverage TOML files for configuration, it might better suit your project to use JSON instead:
$ semantic-release generate-config -f json
If you would like to add JSON configuration to a shared file, e.g. package.json
, you
can then simply add the output from this command as a top-level key to the file.
Note: Because there is no “null” or “nil” concept in TOML (see the relevant
GitHub issue), configuration settings which are None
by default are omitted
from the default configuration.
See also
Options:¶
-f/--format [FORMAT]
¶
The format that the default configuration should be generated in. Valid choices are
toml
and json
(case-insensitive).
Default: toml
--pyproject
¶
If used alongside --format json
, this option has no effect. When using
--format=toml
, if specified the configuration will sit under a top-level key
of tool.semantic_release
to comply with PEP 518; otherwise, the configuration
will sit under a top-level key of semantic_release
.
semantic-release changelog
¶
Generate and optionally publish a changelog for your project. The changelog is generated based on a template which can be customized.
Python Semantic Release uses Jinja as its templating engine; as a result templates need to be written according to the Template Designer Documentation.
See also
Options:¶
--post-to-release-tag [TAG]
¶
If supplied, attempt to find a release in the remote VCS corresponding to the Git tag
TAG
, and post the generated changelog to that release. If the tag exists but no
corresponding release is found in the remote VCS, then Python Semantic Release will
attempt to create one.
If using this option, the relevant authentication token must be supplied via the relevant environment variable. For more information, see Creating VCS Releases.
Strict Mode¶
Strict Mode is enabled by use of the strict parameter to the main command for Python Semantic Release. Strict Mode alters the behaviour of Python Semantic Release when certain conditions are encountered that prevent Python Semantic Release from performing an action. Typically, this will result in a warning becoming an error, or a different exit code (0 vs non-zero) being produced when Python Semantic Release exits early.
For example:
#!/usr/bin/bash
set -euo pipefail
git checkout $NOT_A_RELEASE_BRANCH
pip install \
black \
isort \
twine \
pytest \
python-semantic-release
isort . # sort imports
black . # format the code
pytest # test the code
semantic-release --strict version # ERROR - not a release branch
twine upload dist/* # publish the code
Using Strict Mode with the --strict
flag ensures this simple pipeline will fail
while running semantic-release
, as the non-zero exit code will cause it to stop
when combined with the -e
option.
Without Strict Mode, the semantic-release
command will exit with code 0, causing
the above pipeline to continue.
The specific effects of enabling Strict Mode are detailed below.
Non-Release Branches¶
When running in Strict Mode, invoking Python Semantic Release on a non-Release branch will cause an error with a non-zero exit code. This means that you can prevent an automated script from running further against branches you do not want to release from, for example in multibranch CI pipelines.
Running without Strict Mode will allow subsequent steps in the pipeline to also execute, but be aware that certain actions that Python Semantic Release may perform for you will likely not have been carried out, such as writing to files or creating a git commit in your repository.
See also
Version Already Released/No Release To Be Made¶
When Strict Mode is not enabled and Python Semantic Release identifies that no release needs to be made, it will exit with code 0. You can cause Python Semantic Release to raise an error if no release needs to be made by enabling Strict Mode.
Configuration¶
Configuration is read from a file which can be specified using the --config option to semantic-release. Python Semantic Release currently supports a configuration in either TOML or JSON format, and will attempt to auto-detect and parse either format.
When using a JSON-format configuration file, Python Semantic Release looks for its
settings beneath a top-level semantic_release
key; when using a TOML-format
configuration file, Python Semantic Release first checks for its configuration under
the table [tool.semantic_release]
(in line with the convention for Python tools to
require their configuration under the top-level tool
table in their
pyproject.toml
file), followed by [semantic_release]
, which may be more desirable
if using a file other than the default pyproject.toml
for configuration.
The examples on this page are given in TOML format, however there is no limitation on using JSON instead. In fact, if you would like to convert any example below to its JSON equivalent, the following commands will do this for you (in Bash):
export TEXT="<the TOML to convert>"
cat <<EOF | python3
import tomlkit, json
print(json.dumps(tomlkit.loads('''$TEXT'''), indent=4))
EOF
A note on null¶
In TOML, there is no such thing as a “null” or “nil” value, and this isn’t planned
as a language feature according to the relevant GitHub issue.
In Python Semantic Release, options which default to None
are inferred from the
relevant configuration settings not being present at all in your configuration.
Because of this limitation, it’s currently not possible to explicitly specify those
settings as “null” in TOML-format configuration. Technically it is possible in
JSON-format configuration, but it’s recommended to keep consistency and just omit
the relevant settings.
Environment Variables¶
Some settings are best pulled from environment variables rather than being stored in plaintext in your configuration file. Python Semantic Release can be configured to look for an environment variable value to use for a given setting, but this feature is not available for all settings. In order to use an environment variable for a setting, you must indicate in your configuration file the name of the environment variable to use.
The traditional and most common use case for environment variable use is for passing
authentication tokens to Python Semantic Release. You do NOT want to hard code your
authentication token in your configuration file, as this is a security risk. A plaintext
token in your configuration file could be exposed to anyone with access to your repository,
including long after its deleted if a token is in your git history. Instead, define the name
of the environment variable which contains your remote.token,
such as GH_TOKEN
, in your configuration file, and Python Semantic Release will do the
rest, as seen below.
[semantic_release.remote.token]
env = "GH_TOKEN"
Given basic TOML syntax compatibility, this is equivalent to:
[semantic_release.remote]
token = { env = "GH_TOKEN" }
The general format for specifying that some configuration should be sourced from an environment variable is:
[semantic_release.variable]
env = "ENV_VAR"
default_env = "FALLBACK_ENV_VAR"
default = "default value"
- In this structure:
env
represents the environment variable that Python Semantic Release will search fordefault_env
is a fallback environment variable to read in case the variable specified byenv
is not set. This is optional - if not specified then no fallback will be used.default
is a default value to use in case the environment variable specified byenv
is not set. This is optional - ifdefault
is not specified then the environment variable specified byenv
is considered required.
semantic_release
settings¶
The following sections outline all the definitions and descriptions of each supported configuration setting. If there are type mis-matches, PSR will throw validation errors upon load. If a setting is not provided, than PSR will fill in the value with the default value.
Python Semantic Release expects a root level key to start the configuration definition. Make sure to use the correct root key dependending on the configuration format you are using.
Note
If you are using pyproject.toml
, this heading should include the tool` prefix
as specified within PEP 517, resulting in [tool.semantic_release]
.
Note
If you are using a releaserc.toml
, use [semantic_release]
as the root key
Note
If you are using a releaserc.json
, semantic_release
must be the root key in the
top level dictionary.
allow_zero_version
¶
Type: bool
This flag controls whether or not Python Semantic Release will use version
numbers aligning with the 0.x.x
pattern.
If set to true
and starting at 0.0.0
, a minor bump would set the
next version as 0.1.0
whereas a patch bump would set the next version as
0.0.1
. A breaking change (ie. major bump) would set the next version as
1.0.0
unless the major_on_zero is set to false
.
If set to false
, Python Semantic Release will consider the first possible
version to be 1.0.0
, regardless of patch, minor, or major change level.
Additionally, when allow_zero_version
is set to false
,
the major_on_zero setting is ignored.
Default: true
assets
¶
Type: list[str]
One or more paths to additional assets that should committed to the remote repository in addition to any files modified by writing the new version.
Default: []
branches
¶
This setting is discussed in more detail at Multibranch Releases
Default:
[semantic_release.branches.main]
match = "(main|master)"
prerelease_token = "rc"
prerelease = false
build_command
¶
Type: Optional[str]
Command to use to build the current project during semantic-release version.
Python Semantic Release will execute the build command in the OS default
shell with a subset of environment variables. PSR provides the variable
NEW_VERSION
in the environment with the value of the next determined
version. The following table summarizes all the environment variables that
are passed on to the build_command
runtime if they exist in the parent
process.
If you would like to pass additional environment variables to your build command, see build_command_env.
Variable Name |
Description |
---|---|
CI |
Pass-through |
BITBUCKET_CI |
|
GITHUB_ACTIONS |
Pass-through |
GITEA_ACTIONS |
Pass-through |
GITLAB_CI |
Pass-through |
HOME |
Pass-through |
NEW_VERSION |
Semantically determined next version (ex. |
PATH |
Pass-through |
PSR_DOCKER_GITHUB_ACTION |
Pass-through |
VIRTUAL_ENV |
Pass-through |
Default: None
(not specified)
build_command_env
¶
Type: Optional[list[str]]
List of environment variables to include or pass-through on to the build command that executes during semantic-release version.
This configuration option allows the user to extend the list of environment variables from the table above in build_command. The input is a list of strings where each individual string handles a single variable definition. There are two formats accepted and are detailed in the following table:
FORMAT |
Description |
---|---|
|
Detects value from the PSR process environment, and passes value to
|
|
Sets variable name to value inside of |
Note
Although variable name capitalization is not required, it is recommended as to be in-line with the POSIX-compliant recommendation for shell variable names.
Default: None
(not specified)
changelog
¶
This section outlines the configuration options available that modify changelog generation.
Note
pyproject.toml: [tool.semantic_release.changelog]
releaserc.toml: [semantic_release.changelog]
releaserc.json: { "semantic_release": { "changelog": {} } }
changelog_file
¶
Type: str
Specify the name of the changelog file (after template rendering has taken place).
Default: "CHANGELOG.md"
environment
¶
Note
This section of the configuration contains options which customize the template environment used to render templates such as the changelog. Most options are passed directly to the jinja2.Environment constructor, and further documentation one these parameters can be found there.
Note
pyproject.toml: [tool.semantic_release.changelog.environment]
releaserc.toml: [semantic_release.changelog.environment]
releaserc.json: { "semantic_release": { "changelog": { "environment": {} } } }
autoescape
¶
Type: Union[str, bool]
If this setting is a string, it should be given in module:attr
form; Python
Semantic Release will attempt to dynamically import this string, which should
represent a path to a suitable callable that satisfies the following:
As of Jinja 2.4 this can also be a callable that is passed the template name and has to return
True
orFalse
depending on autoescape should be enabled by default.
The result of this dynamic import is passed directly to the jinja2.Environment constructor.
If this setting is a boolean, it is passed directly to the jinja2.Environment constructor.
Default: true
block_start_string
¶
Type: str
This setting is passed directly to the jinja2.Environment constructor.
Default: "{%"
block_end_string
¶
Type: str
This setting is passed directly to the jinja2.Environment constructor.
Default: "%}"
comment_end_string
¶
Type: str
This setting is passed directly to the jinja2.Environment constructor.
Default: "#}"
extensions
¶
Type: list[str]
This setting is passed directly to the jinja2.Environment constructor.
Default: []
keep_trailing_newline
¶
Type: bool
This setting is passed directly to the jinja2.Environment constructor.
Default: false
line_comment_prefix
¶
Type: Optional[str]
This setting is passed directly to the jinja2.Environment constructor.
Default: None
(not specified)
line_statement_prefix
¶
Type: Optional[str]
This setting is passed directly to the jinja2.Environment constructor.
Default: None
(not specified)
lstrip_blocks
¶
Type: bool
This setting is passed directly to the jinja2.Environment constructor.
Default: false
newline_sequence
¶
Type: Literal["\n", "\r", "\r\n"]
This setting is passed directly to the jinja2.Environment constructor.
Default: "\n"
trim_blocks
¶
Type: bool
This setting is passed directly to the jinja2.Environment constructor.
Default: false
variable_start_string
¶
Type: str
This setting is passed directly to the jinja2.Environment constructor.
Default: "{{"
variable_end_string
¶
Type: str
This setting is passed directly to the jinja2.Environment constructor.
Default: "}}"
exclude_commit_patterns
¶
Type: list[str]
Any patterns specified here will be excluded from the commits which are available to your changelog. This allows, for example, automated commits to be removed if desired. Python Semantic Release also removes its own commits from the Changelog via this mechanism; therefore if you change the automated commit message that Python Semantic Release uses when making commits, you may wish to add the old commit message pattern here.
The patterns in this list are treated as regular expressions.
Default: []
template_dir
¶
Type: str
If given, specifies a directory of templates that will be rendered during creation of the changelog. If not given, the default changelog template will be used.
This option is discussed in more detail at Changelog Templates
Default: "templates"
commit_message
¶
Type: str
Commit message to use when making release commits. The message can use {version}
as a format key, in which case the version being released will be formatted into
the message.
If at some point in your project’s lifetime you change this, you may wish to consider, adding the old message pattern(s) to exclude_commit_patterns.
Default: "{version}\n\nAutomatically generated by python-semantic-release"
commit_parser
¶
Type: str
Specify which commit parser Python Semantic Release should use to parse the commits within the Git repository.
- Built-in parsers:
angular
- AngularCommitParseremoji
- EmojiCommitParserscipy
- ScipyCommitParsertag
- TagCommitParser
You can set any of the built-in parsers by their keyword but you can also specify
your own commit parser in module:attr
form.
For more information see Commit Parsing.
Default: "angular"
commit_parser_options
¶
Type: dict[str, Any]
These options are passed directly to the parser_options
method of
the commit parser, without validation
or transformation.
For more information, see Parser Options.
The default value for this setting depends on what you specify as
commit_parser. The table below outlines
the expections from commit_parser
value to default options value.
|
Default |
|
---|---|---|
|
-> |
[semantic_release.commit_parser_options]
allowed_types = [
"build", "chore", "ci", "docs", "feat", "fix",
"perf", "style", "refactor", "test"
]
minor_types = ["feat"]
patch_types = ["fix", "perf"]
|
|
-> |
[semantic_release.commit_parser_options]
major_tags = [":boom:"]
minor_tags = [
":sparkles:", ":children_crossing:", ":lipstick:",
":iphone:", ":egg:", ":chart_with_upwards_trend:"
]
patch_tags = [
":ambulance:", ":lock:", ":bug:", ":zap:", ":goal_net:",
":alien:", ":wheelchair:", ":speech_balloon:", ":mag:",
":apple:", ":penguin:", ":checkered_flag:", ":robot:",
":green_apple:"
]
|
|
-> |
[semantic_release.commit_parser_options]
allowed_tags = [
"API", "DEP", "ENH", "REV", "BUG", "MAINT", "BENCH",
"BLD", "DEV", "DOC", "STY", "TST", "REL", "FEAT", "TEST",
]
major_tags = ["API",]
minor_tags = ["DEP", "DEV", "ENH", "REV", "FEAT"]
patch_tags = ["BLD", "BUG", "MAINT"]
|
|
-> |
[semantic_release.commit_parser_options]
minor_tag = ":sparkles:"
patch_tag = ":nut_and_bolt:"
|
|
-> |
|
Default: ParserOptions { ... }
, where ...
depends on
commit_parser as indicated above.
logging_use_named_masks
¶
Type: bool
Whether or not to replace secrets identified in logging messages with named masks identifying which secrets were replaced, or use a generic string to mask them.
Default: false
major_on_zero
¶
Type: bool
This flag controls whether or not Python Semantic Release will increment the major
version upon a breaking change when the version matches 0.y.z
. This value is
set to true
by default, where breaking changes will increment the 0
major
version to 1.0.0
like normally expected.
If set to false
, major (breaking) releases will increment the minor digit of the
version while the major version is 0
, instead of the major digit. This allows for
continued breaking changes to be made while the major version remains 0
.
From the Semantic Versioning Specification:
Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
When you are ready to release a stable version, set major_on_zero
to true
and
run Python Semantic Release again. This will increment the major version to 1.0.0
.
When allow_zero_version is set to false
, this setting is ignored.
Default: true
publish
¶
This section defines configuration options that modify semantic-release publish.
Note
pyproject.toml: [tool.semantic_release.publish]
releaserc.toml: [semantic_release.publish]
releaserc.json: { "semantic_release": { "publish": {} } }
dist_glob_patterns
¶
Type: list[str]
Upload any files matching any of these globs to your VCS release. Each item in this list should be a string containing a Unix-style glob pattern.
Default: ["dist/*"]
upload_to_vcs_release
¶
Type: bool
If set to true
, upload any artifacts matched by the
dist_glob_patterns to the release created
in the remote VCS corresponding to the latest tag. Artifacts are only uploaded if
release artifact uploads are supported by the VCS type.
Default: true
remote
¶
The remote configuration is a group of settings that configure PSR’s integration with remote version control systems.
Note
pyproject.toml: [tool.semantic_release.remote]
releaserc.toml: [semantic_release.remote]
releaserc.json: { "semantic_release": { "remote": {} } }
api_domain
¶
Type: Optional[str | Dict['env', str]]
The hosting domain for the API of your remote HVCS if different than the domain
.
Generally, this will be used to specify a separate subdomain that is used for API
calls rather than the primary domain (ex. api.github.com
).
Most on-premise HVCS installations will NOT use this setting! Whether or not this value is used depends on the HVCS configured (and your server administration) in the remote.type setting and used in tadem with the remote.domain setting.
When using a custom remote.domain and a HVCS remote.type that is configured with a separate domain or sub-domain for API requests, this value is used to configure the location of API requests that are sent from PSR.
Most on-premise or self-hosted HVCS environments will use a path prefix to handle inbound API requests, which means this value will ignored.
PSR knows the expected api domains for known cloud services and their associated
api domains which means this value is not necessary to explicitly define for services
as bitbucket.org
, and github.com
.
Including the protocol schemes, such as https://
, for the API domain is optional.
Secure HTTPS
connections are assumed unless the setting of
remote.insecure is True
.
Default: None
domain
¶
Type: Optional[str | Dict['env', str]]
The host domain for your HVCS server. This setting is used to support on-premise installations of HVCS providers with custom domain hosts.
If you are using the official domain of the associated
remote.type, this value is not required. PSR will use the
default domain value for the remote.type when not specified.
For example, when remote.type="github"
is specified the default domain of
github.com
is used.
Including the protocol schemes, such as https://
, for the domain value is optional.
Secure HTTPS
connections are assumed unless the setting of
remote.insecure is True
.
This setting also supports reading from an environment variable for ease-of-use
in CI pipelines. See Environment Variable for
more information. Depending on the remote.type, the default
environment variable for the default domain’s CI pipeline environment will automatically
be checked so this value is not required in default environments. For example, when
remote.type="gitlab"
is specified, PSR will look to the CI_SERVER_URL
environment
variable when remote.domain
is not specified.
Default: None
See also
ignore_token_for_push
¶
Type: bool
If set to True
, ignore the authentication token when pushing changes to the remote.
This is ideal, for example, if you already have SSH keys set up which can be used for
pushing.
Default: False
insecure
¶
Type: bool
Insecure is used to allow non-secure HTTP
connections to your HVCS server. If set to
True
, any domain value passed will assume http://
if it is not specified and allow
it. When set to False
(implicitly or explicitly), it will force https://
communications.
When a custom domain
or api_domain
is provided as a configuration, this flag governs
the protocol scheme used for those connections. If the protocol scheme is not provided in
the field value, then this insecure
option defines whether HTTP
or HTTPS
is
used for the connection. If the protocol scheme is provided in the field value, it must
match this setting or it will throw an error.
The purpose of this flag is to prevent any typos in provided domain
and api_domain
values that accidently specify an insecure connection but allow users to toggle the protection
scheme off when desired.
Default: False
name
¶
Type: str
Name of the remote to push to using git push -u $name <branch_name>
Default: "origin"
token
¶
Type: Optional[str | Dict['env', str]]
Environment Variable from which to source the
authentication token for the remote VCS. Common examples include "GH_TOKEN"
,
"GITLAB_TOKEN"
or "GITEA_TOKEN"
, however, you may choose to use a custom
environment variable if you wish.
Note
By default, this is a mandatory environment variable that must be set before using any functionality that requires authentication with your remote VCS. If you are using this token to enable push access to the repository, it must also be set before attempting to push.
If your push access is enabled via SSH keys instead, then you do not need to set this environment variable in order to push the version increment, changelog and modified source code assets to the remote using semantic-release version. However, you will need to disable release creation using the --vcs-release/--no-vcs-release option, among other options, in order to use Python Semantic Release without configuring the environment variable for your remote VCS authentication token.
The default value for this setting depends on what you specify as remote.type. Review the table below to see what the default token value will be for each remote type.
|
Default |
|
---|---|---|
|
-> |
|
|
-> |
|
|
-> |
|
|
-> |
|
Default: { env = "<envvar name>" }
, where <envvar name>
depends on
remote.type as indicated above.
type
¶
Type: Literal["bitbucket", "gitea", "github", "gitlab"]
The type of the remote VCS. Currently, Python Semantic Release supports "github"
,
"gitlab"
, "gitea"
and "bitbucket"
. Not all functionality is available with all
remote types, but we welcome pull requests to help improve this!
Default: "github"
url
¶
Type: Optional[str | Dict['env', str]]
An override setting used to specify the remote upstream location of git push
.
Not commonly used! This is used to override the derived upstream location when the desired push location is different than the location the repository was cloned from.
This setting will override the upstream location url that would normally be derived from the remote.name location of your git repository.
Default: None
tag_format
¶
Type: str
Specify the format to be used for the Git tag that will be added to the repo during a release invoked via semantic-release version. The format string is a regular expression, which also must include the format keys below, otherwise an exception will be thrown. It may include any of the optional format keys, in which case the contents described will be formatted into the specified location in the Git tag that is created.
For example, "(dev|stg|prod)-v{version}"
is a valid tag_format
matching tags such
as:
dev-v1.2.3
stg-v0.1.0-rc.1
prod-v2.0.0+20230701
This format will also be used for parsing tags already present in the repository into semantic versions; therefore if the tag format changes at some point in the repository’s history, historic versions that no longer match this pattern will not be considered as versions.
Format Key |
Mandatory |
Contents |
---|---|---|
|
Yes |
The new semantic version number, for example |
Tags which do not match this format will not be considered as versions of your project.
Default: "v{version}"
version_toml
¶
Type: list[str]
Similar to version_variables, but allows the version number to be
identified safely in a toml file like pyproject.toml
, with each entry using
dotted notation to indicate the key for which the value represents the version:
[semantic_release]
version_toml = [
"pyproject.toml:tool.poetry.version",
]
Default: []
version_variables
¶
Type: list[str]
Each entry represents a location where the version is stored in the source code,
specified in file:variable
format. For example:
[semantic_release]
version_variables = [
"semantic_release/__init__.py:__version__",
"docs/conf.py:version",
]
Default: []
Commit Parsing¶
The semver level that should be bumped on a release is determined by the commit messages since the last release. In order to be able to decide the correct version and generate the changelog, the content of those commit messages must be parsed. By default this package uses a parser for the Angular commit message style:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
The body or footer can begin with BREAKING CHANGE:
followed by a short
description to create a major release.
Note
Python Semantic Release is able to parse more than just the body and footer sections (in fact, they are processed in a loop so you can write as many paragraphs as you need). It also supports having multiple breaking changes in one commit.
However, other tools may not do this, so if you plan to use any similar programs then you should try to stick to the official format.
More information about the style can be found in the angular commit guidelines.
See also
Built-in Commit Parsers¶
The following parsers are built in to Python Semantic Release:
semantic_release.commit_parser.AngularCommitParser
¶
The default parser, which uses the Angular commit style with the following differences:
Multiple
BREAKING CHANGE:
paragraphs are supported
revert
is not currently supported
The default configuration options for
semantic_release.commit_parser.AngularCommitParser
are:
[tool.semantic_release.commit_parser_options]
allowed_tags = [
"build",
"chore",
"ci",
"docs",
"feat",
"fix",
"perf",
"style",
"refactor",
"test",
]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
semantic_release.history.EmojiCommitParser
¶
Parser for commits using one or more emojis as tags in the subject line.
If a commit contains multiple emojis, the one with the highest priority (major, minor, patch, none) or the one listed first is used as the changelog section for that commit. Commits containing no emojis go into an “Other” section.
The default settings are for Gitmoji.
The default configuration options for
semantic_release.commit_parser.EmojiCommitParser
are:
[tool.semantic_release.commit_parser_options]
major_tags = [":boom:"]
minor_tags = [
":sparkles:",
":children_crossing:",
":lipstick:",
":iphone:",
":egg:",
":chart_with_upwards_trend:",
]
patch_tags = [
":ambulance:",
":lock:",
":bug:",
":zap:",
":goal_net:",
":alien:",
":wheelchair:",
":speech_balloon:",
":mag:",
":apple:",
":penguin:",
":checkered_flag:",
":robot:",
":green_apple:",
]
semantic_release.history.scipy_parser
¶
A parser for scipy-style commits with the following differences:
Beginning a paragraph inside the commit with
BREAKING CHANGE
declares a breaking change. MultipleBREAKING CHANGE
paragraphs are supported.A scope (following the tag in parentheses) is supported
The default configuration options for
semantic_release.commit_parser.ScipyCommitParser
are:
[tool.semantic_release.commit_parser_options]
allowed_tags = [
"API",
"DEP",
"ENH",
"REV",
"BUG",
"MAINT",
"BENCH",
"BLD",
"DEV",
"DOC",
"STY",
"TST",
"REL",
"FEAT",
"TEST",
]
major_tags = ["API"]
minor_tags = ["DEP", "DEV", "ENH", "REV", "FEAT"]
patch_tags = ["BLD", "BUG", "MAINT"]
semantic_release.history.TagCommitParser
¶
The original parser from v1.0.0 of Python Semantic Release. Similar to the emoji parser above, but with less features.
The default configuration options for
semantic_release.commit_parser.TagCommitParser
are:
[tool.semantic_release.commit_parser_options]
minor_tag = ":sparkles:"
patch_tag = ":nut_and_bolt:"
Writing your own parser¶
If you would prefer to use an alternative commit style, for example to adjust the
different type
values that are associated with a particular commit, this is
possible.
The commit_parser option, if set to a string which
does not match one of Python Semantic Release’s inbuilt commit parsers, will be
used to attempt to dynamically import a custom commit parser class. As such you will
need to ensure that your custom commit parser is import-able from the environment in
which you are running Python Semantic Release. The string should be structured in the
standard module:attr
format; for example, to import the class MyCommitParser
from the file custom_parser.py
at the root of your repository, you should specify
"commit_parser=custom_parser:MyCommitParser"
in your configuration, and run the
semantic-release
command line interface from the root of your repository. Equally
you can ensure that the module containing your parser class is installed in the same
virtual environment as semantic-release.
If you can run python -c "from $MODULE import $CLASS"
successfully, specifying
commit_parser="$MODULE:$CLASS"
is sufficient. You may need to set the
PYTHONPATH
environment variable to the directory containing the module with
your commit parser.
Python Semantic Release provides several building blocks to help you write your parser. To maintain compatibility with how Python Semantic Release will invoke your parser, you should use the appropriate object as described below, or create your own object as a subclass of the original which maintains the same interface. Type parameters are defined where appropriate to assist with static type-checking.
Tokens¶
The tokens built into Python Semantic Release’s commit parsing mechanism are inspired
by both the error-handling mechanism in Rust’s error handling and its
implementation in black. It is documented that catching exceptions in Python is
slower than the equivalent guard implemented using if/else
checking when
exceptions are actually caught, so although try/except
blocks are cheap if no
exception is raised, commit parsers should always return an object such as
semantic_release.ParseError
instead of raising an error immediately.
This is to avoid catching a potentially large number of parsing errors being caught
as the commit history of a repository is being parsed. Python Semantic Release does
not raise an exception if a commit cannot be parsed.
Python Semantic Release uses semantic_release.ParsedCommit
as the return type of a successful parse operation, and semantic_release.ParseError
as the return type from an unsuccessful parse of a commit. semantic_release.ParsedCommit
is a namedtuple which has the following fields:
bump: a
semantic_release.LevelBump
indicating what type of change this commit introduces.type: the type of the commit as a string, per the commit message style. This is up to the parser to implement; for example, the
semantic_release.commit_parser.EmojiCommitParser
parser fills this field with the emoji representing the most significant change for the commit. The field is named after the representation in the Angular commit specification.scope: The scope, as a string, parsed from the commit. Commit styles which do not have a meaningful concept of “scope” should fill this field with an empty string.
descriptions: A list of paragraphs (strings) (delimited by a double-newline) from the commit message.
breaking_descriptions: A list of paragraphs (strings) which are deemed to identify and describe breaking changes by the parser. An example would be a paragraph which begins with the text
BREAKING CHANGE:
.commit: The original commit object that was parsed.
semantic_release.ParseError
is a namedtuple which has the following fields:
commit: The original commit object that was parsed.
error: A string with a meaningful error message as to why the commit parsing failed.
In addition, semantic_release.ParseError
implements an additional method, raise_error
.
This method raises a semantic_release.CommitParseError
with the message contained in the
error
field, as a convenience.
ParsedCommit
and ParseError
objects also make the following
attributes available, each implemented as a property
which is computed, as a
convenience for template authors - therefore custom implementations should ensure
these properties can also be computed:
message: the
message
attribute of thecommit
; where the message is of typebytes
this should be decoded to aUTF-8
string.hexsha: the
hexsha
attribute of thecommit
, representing its hash.short_hash: the first 7 characters of the
hexsha
attribute of thecommit
.
In Python Semantic Release, the class semantic_release.ParseResult
is defined as ParseResultType[ParsedCommit, ParseError]
, as a convenient shorthand.
semantic_release.ParseResultType
is a generic type, which
is the Union
of its two type parameters. One of the types in this union should be the
type returned on a successful parse of the commit
, while the other should be the
type returned on an unsuccessful parse of the commit
.
A custom parser result type, therefore, could be implemented as follows:
MyParsedCommit
subclassesParsedCommit
MyParseError
subclassesParseError
MyParseResult = ParseResultType[MyParsedCommit, MyParseError]
Internally, Python Semantic Release uses isinstance
to determine if the result
of parsing a commit was a success or not, so you should check that your custom result
and error types return True
from isinstance(<object>, ParsedCommit)
and
isinstance(<object>, ParseError)
respectively.
While it’s not advisable to remove any of the fields that are available in the built-in
token types, currently only the bump
field of the successful result type is used to
determine how the version should be incremented as part of this release. However, it’s
perfectly possible to add additional fields to your tokens which can be populated by
your parser; these fields will then be available on each commit in your
changelog template, so you can make additional information
available.
Parser Options¶
To provide options to the commit parser which is configured in the configuration file, Python Semantic Release includes a semantic_release.ParserOptions
class. Each parser built into Python Semantic Release has a corresponding “options” class, which
subclasses semantic_release.ParserOptions
.
The configuration in commit_parser_options is passed to the “options” class which is specified by the configured commit_parser - more information on how this is specified is below.
The “options” class is used to validate the options which are configured in the repository, and to provide default values for these options where appropriate.
If you are writing your own parser, you should accompany it with an “options” class
which accepts the appropriate keyword arguments. This class’ __init__
method should
store the values that are needed for parsing appropriately.
Commit Parsers¶
The commit parsers that are built into Python Semantic Release implement an instance
method called parse
, which takes a single parameter commit
of type
git.objects.commit.Commit, and returns the type
semantic_release.ParseResultType
.
To be compatible with Python Semantic Release, a commit parser must subclass
semantic_release.CommitParser
. A subclass must implement
the following:
A class-level attribute
parser_options
, which must be set tosemantic_release.ParserOptions
or a subclass of this.An
__init__
method which takes a single parameter,options
, that should be of the same type as the class’parser_options
attribute.A method,
parse
, which takes a single parametercommit
that is of type git.objects.commit.Commit, and returnssemantic_release.token.ParseResult
, or a subclass of this.
By default, the constructor for semantic_release.CommitParser
will set the options
parameter on the options
attribute of the parser, so there is no need to override
this in order to access self.options
during the parse
method. However, if you
have any parsing logic that needs to be done only once, it may be a good idea to
perform this logic during parser instantiation rather than inside the parse
method.
The parse method will be called once per commit in the repository’s history during
parsing, so the effect of slow parsing logic within the parse
method will be
magnified significantly for projects with sizeable Git histories.
Commit Parsers have two type parameters, “TokenType” and “OptionsType”. The first
is the type which is returned by the parse
method, and the second is the type
of the “options” class for this parser.
Therefore, a custom commit parser could be implemented via:
class MyParserOptions(semantic_release.ParserOptions):
def __init__(self, message_prefix: str) -> None:
self.prefix = message_prefix * 2
class MyCommitParser(
semantic_release.CommitParser[semantic_release.ParseResult, MyParserOptions]
):
def parse(self, commit: git.objects.commit.Commit) -> semantic_release.ParseResult:
...
Changelog Templates¶
Warning
If you have an existing changelog in the location you have configured with the changelog_file setting, or if you have a template inside your template directory which will render to the location of an existing file, Python Semantic Release will overwrite the contents of this file.
Please make sure to refer to Migrating an Existing Changelog.
Python Semantic Release can write a changelog for your project. By default, it uses an in-built template; once rendered this will be written to the location you configure with the changelog_file setting.
However, Python Semantic Release is also capable of rendering an entire directory tree of templates during the changelog generation process. This directory is specified using the template directory setting.
Python Semantic Release uses Jinja as its template engine, so you should refer to the Template Designer Documentation for guidance on how to customize the appearance of the files which are rendered during the release process. If you would like to customize the template environment itself, then certain options are available to you via changelog environment configuration.
Changelogs are rendered during the semantic-release version and semantic-release changelog commands. You can disable changelog generation entirely during the semantic-release version command by providing the –no-changelog command-line option.
The changelog template is re-rendered on each release.
Template Rendering¶
Directory Structure:¶
If you don’t want to set up your own custom changelog template, you can have Python
Semantic Release use its in-built template. If you would like to customize the
appearance of the changelog, or to render additional files, then you will need to
create a directory within your repository and set the template_dir
setting to the name of this directory. The default name is "templates"
.
Note
It is strongly recommended that you use a dedicated top-level folder for the template directory.
When the templates are rendered, files within the tree are output to the location within your repository that has the same relative path to the root as the relative path of the template to the templates directory.
Templates are identified by giving a .j2
extension to the template file. Any such
templates have the .j2
extension removed from the target file. Therefore, to render
an output file foo.csv
, you should create a template called foo.csv.j2
within
your template directory.
Note
A file within your template directory which does not end in .j2
will not
be treated as a template; it will be copied to its target location without being
rendered by the template engine.
Files within the template directory are excluded from the rendering process if the
file begins with a "."
or if any of the folders containing this file begin with
a "."
.
Directory Structure (Example)¶
Suppose a project sets template_dir to
"templates"
and has the following structure:
example-project
├── src
│ └── example_project
│ └── __init__.py
└── templates
├── CHANGELOG.md.j2
├── .components
│ └── authors.md.j2
├── .macros.j2
├── src
│ └── example_project
│ └── data
│ └── data.json.j2
└── static
└── config.cfg
After running a release with Python Semantic Release, the directory structure of the project will now look like this:
example-project
├── CHANGELOG.md
├── src
│ └── example_project
│ ├── data
│ │ └── data.json
│ └── __init__.py
├── static
│ └── config.cfg
└── templates
├── CHANGELOG.md.j2
├── .components
│ └── authors.md.j2
├── .macros.j2
├── src
│ └── example_project
│ └── data
│ └── data.json.j2
└── static
└── config.cfg
Note that:
There is no top-level
.macros
file created, because this file is excluded from the rendering process.There is no top-level
.components
directory created, because this folder and all files and folders contained within it are excluded from the rendering process.To render data files into the
src/
folder, the path to which the template should be rendered has to be created within thetemplates
directory.The
templates/static
folder is created at the top-level of the project, and the filetemplates/static/config.cfg
is copied, not rendered to the new top-levelstatic
folder.
You may wish to leverage this behaviour to modularise your changelog template, to define macros in a separate file, or to reference static data which you would like to avoid duplicating between your template environment and the remainder of your project.
Template Context¶
Alongside the rendering of a directory tree, Python Semantic Release makes information about the history of the project available within the templating environment in order for it to be used to generate Changelogs and other such documents.
The history of the project is made available via the global variable context
. In
Python terms, context
is a dataclass with the following attributes:
repo_name: str
: the name of the current repository parsed from the Git url.repo_owner: str
: the owner of the current repository parsed from the Git url.hvcs_type: str
: the name of the VCS server type currently configured.history: ReleaseHistory
: asemantic_release.changelog.ReleaseHistory
instance. (See ReleaseHistory)filters: Tuple[Callable[..., Any], ...]
: a tuple of filters for the template environment. These are added to the environment’sfilters
, and therefore there should be no need to access these from thecontext
object inside the template.
The filters provided vary based on the VCS configured and available features:
create_server_url: Callable[[str, str | None, str | None, str | None], str]
: when given a path, prepend the configured vcs server host and url scheme. Optionally you can provide, a auth string, a query string or a url fragment to be normalized into the resulting url. Parameter order is as described above respectively.create_repo_url: Callable[[str, str | None, str | None], str]
: when given a repository path, prepend the configured vcs server host, and repo namespace. Optionally you can provide, an additional query string and/or a url fragment to also put in the url. Parameter order is as described above respectively. This is similar tocreate_server_url
but includes the repo namespace and owner automatically.commit_hash_url: Callable[[str], str]
: given a commit hash, return a URL to the commit in the remote.compare_url: Callable[[str, str], str]
: given a starting git reference and a ending git reference create a comparison url between the two references that can be opened on the remoteissue_url: Callable[[str | int], str]
: given an issue number, return a URL to the issue on the remote vcs.merge_request_url: Callable[[str | int], str]
: given a merge request number, return a URL to the merge request in the remote. This is an alias to thepull_request_url
but only available for the VCS that uses the merge request terminology.pull_request_url: Callable[[str | int], str]
: given a pull request number, return a URL to the pull request in the remote. For remote vcs’ that use merge request terminology, this filter is an alias to themerge_request_url
filter function.
Availability of the documented filters can be found in the table below:
filter - hvcs_type |
bitbucket |
gitea |
github |
gitlab |
---|---|---|---|---|
create_server_url |
✅ |
✅ |
✅ |
✅ |
create_repo_url |
✅ |
✅ |
✅ |
✅ |
commit_hash_url |
✅ |
✅ |
✅ |
✅ |
compare_url |
✅ |
❌ |
✅ |
✅ |
issue_url |
❌ |
✅ |
✅ |
✅ |
merge_request_url |
❌ |
❌ |
❌ |
✅ |
pull_request_url |
✅ |
✅ |
✅ |
✅ |
See also
ReleaseHistory
¶
A ReleaseHistory
instance has two attributes: released
and unreleased
.
The unreleased
attribute is of type Dict[str, List[ParseResult]]
. Each commit
in the current branch’s commit history since the last release on this branch is grouped
by the type
attribute of the ParsedCommit
returned by the commit parser,
or if the parser returned a ParseError
then the result is grouped under the
"unknown"
key.
For this reason, every element of ReleaseHistory.unreleased["unknown"]
is a
ParseError
, and every element of every other value in ReleaseHistory.unreleased
is of type ParsedCommit
.
Typically, commit types will be "feature"
, "fix"
, "breaking"
, though the
specific types are determined by the parser. For example, the
semantic_release.commit_parser.EmojiCommitParser
uses a textual
representation of the emoji corresponding to the most significant change introduced
in a commit (e.g. ":boom:"
) as the different commit types. As a template author,
you are free to customise how these are presented in the rendered template.
Note
If you are using a custom commit parser following the guide at
Writing your own parser, your custom implementations of
semantic_release.ParseResult
, semantic_release.ParseError
and semantic_release.ParsedCommit
will be used in place of the built-in
types.
The released
attribute is of type Dict[Version, Release]
. The keys of this
dictionary correspond to each version released within this branch’s history, and
are of type semantic_release.Version
. You can use the as_tag()
method to
render these as the Git tag that they correspond to inside your template.
A Release
object has an elements
attribute, which has the same
structure as the unreleased
attribute of a ReleaseHistory
; that is,
elements
is of type Dict[str, List[ParseResult]]
, where every element
of elements["unknown"]
is a ParseError
, and elements of every other
value correspond to the type
attribute of the ParsedCommit
returned
by the commit parser.
The commits represented within each ReleaseHistory.released[version].elements
grouping are the commits which were made between version
and the release
corresponding to the previous version.
That is, given two releases Version(1, 0, 0)
and Version(1, 1, 0)
,
ReleaseHistory.released[Version(1, 0, 0)].elements
contains only commits
made after the release of Version(1, 0, 0)
up to and including the release
of Version(1, 1, 0)
.
To maintain a consistent order of subsections in the changelog headed by the commit type, it’s recommended to use Jinja’s dictsort filter.
Each Release
object also has the following attributes:
tagger: git.Actor
: The tagger who tagged the release.committer: git.Actor
: The committer who made the release commit.tagged_date: datetime
: The date and time at which the release was tagged.
Customizing VCS Release Notes¶
The same template rendering mechanism generates the release notes when creating VCS releases:
the in-built template is used by default
create a file named
.release_notes.md.j2
inside the project’s template_dir to customize the release notes
Release Notes Context¶
All of the changelog’s template context is exposed to the Jinja template when rendering the release notes.
Additionally, the following two globals are available to the template:
Release Notes Template Example¶
Below is an example template that can be used to render release notes (it’s similar to GitHub’s automatically generated release notes):
## What's Changed
{% for type_, commits in release["elements"] | dictsort %}
### {{ type_ | capitalize }}
{%- if type_ != "unknown" %}
{% for commit in commits %}
* {{ commit.descriptions[0] }} by {{commit.commit.author.name}} in [`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})
{%- endfor %}{% endif %}{% endfor %}
Changelog Template Example¶
Below is an example template that can be used to render a Changelog:
# CHANGELOG
{% if context.history.unreleased | length > 0 %}
{# UNRELEASED #}
## Unreleased
{% for type_, commits in context.history.unreleased | dictsort %}
### {{ type_ | capitalize }}
{% for commit in commits %}{% if type_ != "unknown" %}
* {{ commit.commit.message.rstrip() }} ([`{{ commit.commit.hexsha[:7] }}`]({{ commit.commit.hexsha | commit_hash_url }}))
{% else %}
* {{ commit.commit.message.rstrip() }} ([`{{ commit.commit.hexsha[:7] }}`]({{ commit.commit.hexsha | commit_hash_url }}))
{% endif %}{% endfor %}{% endfor %}
{% endif %}
{# RELEASED #}
{% for version, release in context.history.released.items() %}
## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
{% for type_, commits in release["elements"] | dictsort %}
### {{ type_ | capitalize }}
{% for commit in commits %}{% if type_ != "unknown" %}
* {{ commit.commit.message.rstrip() }} ([`{{ commit.commit.hexsha[:7] }}`]({{ commit.commit.hexsha | commit_hash_url }}))
{% else %}
* {{ commit.commit.message.rstrip() }} ([`{{ commit.commit.hexsha[:7] }}`]({{ commit.commit.hexsha | commit_hash_url }}))
{% endif %}{% endfor %}{% endfor %}{% endfor %}
Migrating an Existing Changelog¶
If you have an existing changelog that you would like to preserve, it’s recommended
that you add the contents of this file to your changelog template - either directly
or via Jinja’s include
tag. If you would like only the history from your next release onwards to be rendered
into the changelog in addition to the existing changelog, you can add an if statement based upon the versions in
the keys of context.released
.
Multibranch Releases¶
Python Semantic Release supports releases from multiple branches within your Git repository. You can elect to have a branch or set of branches create releases or prereleases. There are no restrictions enforced on how you set up your releases, but be aware that if you create new releases from multiple branches, or prereleases from multiple independent branches using the same prerelease token, there is a chance that Python Semantic Release will calculate the next version to be the same on more than one branch (leading to an error that a Git tag already exists).
Note
A “prerelease token” is the string used to suffix onto the 3-digit form of a full
semantic version. For example, in the version 1.2.3-beta.1
, the prerelease token
is "beta"
Typical strings used for pre-release tokens include “alpha”, “beta”, “dev” and “rc”. These tend to indicate a level of maturity of the software associated with the version, but the specific meaning of each string is up to the project to decide.
Generally, it’s good practice to maintain a single branch from which full releases are made, and one branch at a time for each type of prerelease (alpha, beta, rc, etc).
If you absolutely require tagging and (pre-)releases to take place from multiple branches where there’s a risk that tags could conflict between branches, you can use the –build-metadata command line argument to attach additional information (such as the branch name) to the tag in order to uniquely distinguish it from any other tags that might be calculated against other branches. Such a situation may occur in the following scenario:
O ----------- O <---- feature-1
/ "feat: abc"
/
O -------- O --------------- O <---- main
v1.0.0 v1.1.0
\
O ----------- O <---- feature-2
"feat: 123"
Suppose that Python Semantic Release has been configured to use the same
prerelease token "alpha"
for all feature-*
branches, and the default tag
format "v{version}"
. In this case, running a pre-release from branch feature-1
will recognise that since the last release, 1.1.0
, a feature has been
introduced and therefore the next tag to be applied to feature-1
will be
v1.2.0-alpha.1
.
However, suppose we then try to run a release against feature-2
. This will also
recognise that a feature has been introduced against the last released version of
v1.1.0
and therefore will try to create the tag v1.2.0-alpha.1
, leading to an
error as this tag was already created against feature-1
.
To get around this issue, you can pass the branch name as part of the build metadata:
semantic-release version --build-metadata $(git branch --show-current)
This would lead to the tag v1.2.0-alpha.1+feature-1
and v1.2.0-alpha.1+feature-2
being applied to branches feature-1
and feature-2
, respectively. Note that
“build metadata MUST be ignored” per the semver specification when comparing two
versions, so these two prereleases would be considered equivalent semantic versions,
but when merged to the branch configured to produce full releases (main
), if
released separately the changes from each branch would be released in two versions
that would be considered different according to the semver specification.
Note
If you have tags in your Git repository that are not valid semantic versions (which have then been formatted into your tag_format), these tags will be ignored for the purposes of calculating the next version.
Configuring Multibranch Releases¶
Within your configuration file, you can create one or more groups of branches (“release groups”) that produce a certain type of release. Options are configured at the group level, and the group to use is chosen based on the current branch name against which Python Semantic Release is running.
Each release group is configured as a nested mapping under the
tool.semantic_release.branches
key in pyproject.toml
, or the equivalent
structure in other formats. the mapping requires a single key that is used as a
name for the release group, which can help to identify it in log messages but has
no effect on the behaviour of the release. For example, Python Semantic Release has
only one release group by default with the name main
.
Inside each release group, the following key-value pairs can be set:
Key |
Required |
Default |
Description |
match |
Yes |
N/A |
A Python regular expression to match against the active branch’s name. If the branch name matches the provided regular expression, then this release group is chosen to provide the other configuration settings available. |
prerelease |
No |
|
Whether or not branches in this release group should a prerelease instead of a full release |
prerelease_token |
No |
|
If creating a prerelease, specify the string to be used as a prerelease token in any new versions created against this branch. |
Warning
If two release groups have overlapping “match” patterns, i.e. a the name of a branch could theoretically match both patterns, then the release group which is defined first in your configuration file is used.
Because of this, it’s recommended that you place release groups with more specific match patterns higher up in your configuration file than those with patterns that would match a broader range of branch names.
For example, suppose a project currently on version 1.22.4
is working on a new major version. The
project wants to create a branch called 2.x.x
against which they will develop the new major version,
and they would like to create “release candidate” (“rc”) prereleases from this branch.
There are also a number of new features to integrate, and the project has agreed that all such branches
should be named according to the convention next-{developer initials}-{issue number}
, leading to
branches named similarly to next-bc-prj-123
. The project would like to release with tags that include
some way to identify the branch and date on which the release was made from the tag.
This project would be able to leverage the following configuration to achieve the above requirements from their release configuration:
[tool.semantic_release.branches.main]
match = "(main|master)"
prerelease = false
[tool.semantic_release.branches."2.x.x"]
match = "2.x.x"
prerelease = true
prerelease_token = "rc"
[tool.semantic_release.branches."2.x.x New Features"]
match = "next-\\w+-prj-\\d+"
prerelease = true
prerelease_token = "alpha"
In a CI pipeline, the following command would allow attaching the date and branch name
to the versions that are produced (note this example uses the UNIX date
command):
semantic-release version \
--build-metadata "$(git branch --show-current).$(date +%Y%m%d)"
This would lead to versions such as 1.1.1+main.20221127
or 2.0.0-rc.4+2.x.x.20221201
.
Note
Remember that is always possible to override the release rules configured by using the --major/--minor/--patch/--prerelease and --as-prerelease flags.
Automatic Releases¶
The key point with using this package is to automate your releases and stop worrying about version numbers. Different approaches to automatic releases and publishing with the help of this package can be found below. Using a CI is the recommended approach.
Guides¶
Setting up python-semantic-release on Travis CI¶
This guide expects you to have activated the repository on Travis CI. If this is not the case, please refer to Travis documentation on how to do that.
1. Add python-semantic-release settings¶
See Configuration for details on how to configure Python Semantic Release. Make sure that at least you have set version_variables before continuing.
2. Add environment variables¶
You will need to set up an environment variable in Travis. An easy way to do that is to go to the settings page for your package and add it there. Make sure that the secret toggle is set correctly.
You need to set the GH_TOKEN environment
variable with a personal access token for Github. It will need either repo
or
public_repo
scope depending on whether the repository is private or public.
More information on how to set environment variables can be found on Travis documentation on environment variables.
3. Add travis configuration¶
The following should be added to your .travis.yml
file.
after_success:
- git config --global user.name "semantic-release (via TravisCI)"
- git config --global user.email "semantic-release@travis"
- pip install python-semantic-release
- semantic-release version && semantic-release publish
The first line tells Travis that we want to run the listed tasks after a successful build. The two first lines in after_success will configure git so that python-semantic-release will be able to commit on Travis. The third installs the latest version of python-semantic-release. The last will run the publish command, which will publish a new version if the changes indicate that one is due.
4. Push some changes¶
You are now ready to release automatically on Travis CI on every change to your master branch.
Happy coding!
Setting up python-semantic-release on GitHub Actions¶
Python Semantic Release includes a GitHub Action which runs the version
and
publish
commands. The repository is set to PyPI
. You can read the full set
of inputs available, and their descriptions in the action definition.
Your project’s configuration file will be used as normal.
The GitHub Action provides the following outputs:
Output |
Description |
released |
“true” if a release was made, “false” otherwise |
version |
The newly released version if one was made, otherwise the current version |
tag |
The Git tag corresponding to the “version” output. The format is dictated by your configuration. |
Example Workflow¶
name: Semantic Release
on:
push:
branches:
- master
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
concurrency
is a
beta feature of GitHub Actions
which disallows two or more release jobs to run in parallel. This prevents race
conditions if there are multiple pushes in a short period of time.
If you would like to use Python Semantic Release to create GitHub Releases against
your repository, you will need to allow the additional contents: write
permission.
More information can be found in the permissions for GitHub Apps documentation
Warning
You must set fetch-depth
to 0 when using actions/checkout@v2
, since
Python Semantic Release needs access to the full history to determine whether
a release should be made.
Warning
The GITHUB_TOKEN
secret is automatically configured by GitHub, with the
same permissions as the user who triggered the workflow run. This causes
a problem if your default branch is protected.
You can work around this by storing an administrator’s Personal Access Token
as a separate secret and using that instead of GITHUB_TOKEN
. In this
case, you will also need to pass the new token to actions/checkout
(as
the token
input) in order to gain push access.
Multiple Projects¶
If you have multiple projects stored within a single repository (or your
project is not at the root of the repository), you can pass the
directory
input. The step can be called multiple times to release
multiple projects.
- name: Release Project 1
uses: python-semantic-release/python-semantic-release@master
with:
directory: ./project1
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Release Project 2
uses: python-semantic-release/python-semantic-release@master
with:
directory: ./project2
github_token: ${{ secrets.GITHUB_TOKEN }}
Publish with cronjobs¶
This is for you if for some reason you cannot publish from your CI or you would like releases to drop at a certain interval. Before you start, answer this: Are you sure you do not want a CI to release for you? (high version numbers are not a bad thing).
The guide below is for setting up scheduled publishing on a server. It requires that the user that runs the cronjob has push access to the repository and upload access to an artifact repository.
Create a virtualenv:
virtualenv semantic_release -p `which python3`
Install python-semantic-release:
pip install python-semantic-release
3. Clone the repositories you want to have scheduled publishing.
3. Put the following in publish
:
VENV=semantic_release/bin
$VENV/pip install -U pip python-semantic-release > /dev/null
publish() {
cd $1
git stash -u # ensures that there is no untracked files in the directory
git fetch && git reset --hard origin/master
$VENV/semantic-release version && $VENV/semantic-release publish
cd ..
}
publish <package1>
publish <package2>
Add cronjob:
/bin/bash -c "cd <path> && source semantic_release/bin/activate && ./publish 2>&1 >> releases.log"
Configuring push to Github¶
In order to push to Github and post the changelog to Github the environment variable
GH_TOKEN has to be set. It needs access to the
public_repo
scope for public repositories and repo
for private repositories.
Python Semantic Release GitHub Action¶
Python Semantic Release is also available as a GitHub action.
In order to use Python Semantic Release to create GitHub Releases against your repository, you will need to allow the following permissions for the token generated by GitHub for each job:
id-token: write
contents: write
This can be done using the permissions block in your workflow definition.
Configuring the action can be done in your workflow’s YAML definition. The action provides the following inputs:
Tokens¶
github_token
¶
The GitHub token used to push release notes and new commits/tags.
required: false
Custom Users¶
git_committer_name
¶
The name of the account used to commit. If customized, it must be associated with the provided token.
default: github-actions
required: false
git_committer_email
¶
The email of the account used to commit. If customized, it must be associated with the provided token.
default: actions@github.com>
required: false
ssh_public_signing_key
¶
The public key used to verify a commit. If customized, it must be associated with the same account as the provided token.
required: false
ssh_private_signing_key
¶
The private key used to verify a commit. If customized, it must be associated with the same account as the provided token.
required: false
Additional Options¶
directory
¶
Sub-directory to cd into before running semantic-release
required: false
root_options
¶
Additional options for the main semantic-release
command. Example: -vv --noop
required: false
default: -v
Command Line Options¶
Other inputs which supply additional command-line options to the version command can be optionally supplied, and have the same defaults as their corresponding command line option.
In general, the input for an action corresponding to a command line option has the same
name, with dashes (-
) replaced by underscores.
The command line arguments --prerelease
, --patch
, --minor
and --major
are mutually exclusive, and are supplied via the force
input.
Flags, which require either --<option>
or --no-<option>
to be passed on the
command-line, should be specified using the option name (with dashes replaced by
underscores), and set to the value "true"
to supply --<option>
on the
command-line, and "false"
to specify --no-<option>
.
Any other values are not accepted.
The flag --as-prerelease
is uniquely provided as just the prerelease
flag value.
This is for compatibility reasons.
For command line options requiring a value, set the input to the required value.
For example, to specify --patch --no-push --build-metadata abc123
, you should
provide the following inputs:
---
# ... the rest of the workflow
- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@v8.0.0
with:
# ... other options
force: "patch"
push: "false"
build_metadata: "abc123"
Troubleshooting¶
Check your configuration file for Configuration
Check your Git tags match your tag_format; tags using other formats are ignored during calculation of the next version.
Increasing Verbosity¶
If you are having trouble with Python Semantic Release or would like to see additional information about the actions that it is taking, you can use the top-level -v/--verbose option. This can be supplied multiple times to increase the logging verbosity of the semantic-release command or any of its subcommands during their execution. You can supply this as many times as you like, but supplying more than twice has no effect.
Supply -v/--verbose once for INFO
output, and twice for DEBUG
.
For example:
semantic-release -vv version --print
Note
The -v/--verbose option must be supplied to the top-level
semantic-release
command, before the name of any sub-command.
Warning
The volume of logs when using DEBUG
verbosity may be significantly increased,
compared to INFO
or the default WARNING
, and as a result executing commands
with semantic-release
may be significantly slower than when using DEBUG
.
Note
The provided GitHub action sets the verbosity level to INFO by default.
Contributing¶
If you want to contribute that is awesome. Remember to be nice to others in issues and reviews.
Please remember to write tests for the cool things you create or fix.
Unsure about something? No worries, open an issue.
Commit messages¶
Since python-semantic-release is released with python-semantic-release we need the commit messages to adhere to the angular commit guidelines. If you are unsure how to describe the change correctly Just try and ask in your pr, or ask on gitter. If we think it should be something else or there is a pull-request without tags we will help out in adding or changing them.
Releases¶
This package is released by python-semantic-release on each master build, thus if there are changes that should result in a new release it will happen if the build is green.
Development¶
Install this module and the development dependencies
pip install -e .[dev,mypy,test]
And if you’d like to build the documentation locally
pip install -e .[docs]
sphinx-autobuild --open-browser docs docs/_build/html
Testing¶
To test your modifications locally:
# Run type-checking, all tests across all supported Python versions
tox
# Run all tests for your current installed Python version (with full error output)
pytest -vv tests/
If you need to run tests in a debugger, such as VSCode, you will need to adjust
pyproject.toml
temporarily:
diff --git a/pyproject.toml b/pyproject.toml
[tool.pytest.ini_options]
addopts = [
+ "-n0",
- "-nauto",
"-ra",
"--cache-clear",
- "--cov=semantic_release",
- "--cov-context=test",
- "--cov-report",
- "html:coverage-html",
- "--cov-report",
- "term",
]
Note
The -n0
option disables xdist
’s parallel testing. The removal of the coverage options
is to avoid a bug in pytest-cov
that prevents VSCode from stopping at the breakpoints.
Building¶
This project is designed to be versioned and built by itself using the tool.semantic_release
configuration in pyproject.toml
. The setting tool.semantic_release.build_command
defines
the command to run to build the package.
The following is a copy of the build_command
setting which can be run manually to build the
package locally:
pip install -e .[build]
python -m build .
Contributors¶
Migrating from Python Semantic Release v7¶
Python Semantic Release 8.0.0 introduced a number of breaking changes. The internals have been changed significantly to better support highly-requested features and to streamline the maintenance of the project.
As a result, certain things have been removed, reimplemented differently, or now
exhibit different behaviour to earlier versions of Python Semantic Release. This
page is a guide to help projects to pip install python-semantic-release>=8.0.0
with
fewer surprises.
Python Semantic Release GitHub Action¶
GitHub Action no longer publishes artefacts to PyPI or GitHub Releases¶
Python Semantic Release no longer uploads distributions to PyPI - see Repurposing of version and publish commands. If you are using Python Semantic Release to publish release notes and artefacts to GitHub releases, there is a new GitHub Action upload-to-gh-release which will perform this action for you.
This means the following workflows perform the same actions, and if you are using the former, you will need to modify your workflow to include the steps in the latter.
This workflow is written to use Python Semantic Release v7.33.5:
---
name: Semantic Release
on:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# This action uses Python Semantic Release v7
- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@v7.33.5
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
repository_username: __token__
repository_password: ${{ secrets.PYPI_TOKEN }}
The following workflow achieves the same result using Python Semantic Release v8, the upload-to-gh-release GitHub Action, and the pypa/gh-action-pypi-publish GitHub Action:
---
name: Semantic Release
on:
push:
branches:
- master
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
permissions:
id-token: write
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# This action uses Python Semantic Release v8
- name: Python Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@v8.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# NOTE: DO NOT wrap the conditional in ${{ }} as it will always evaluate to true.
# See https://github.com/actions/runner/issues/1173
if: steps.release.outputs.released == 'true'
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@main
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Removal of pypi_token
, repository_username
and repository_password
inputs¶
Since the library no longer supports publishing to PyPI, the pypi_token
,
repository_username
and repository_password
inputs of the GitHub action have
all been removed. See the above section for how to publish to PyPI using the official
GitHub Action from the Python Packaging Authority (PyPA).
Rename additional_options
to root_options
¶
Because the purposes of the semantic-release version and semantic-release publish commands
have changed, the GitHub action now performs both commands in sequence. For this
reason, and because the usage of the CLI has changed, additional_options
has
been renamed to root_options
to reflect the fact that the options are for the
main semantic-release command group.
Commands¶
Repurposing of version
and publish
commands¶
Python Semantic Release’s primary purpose is to enable automation of correct semantic versioning for software projects. Over the years, this automation has been extended to include other actions such as building/publishing the project and its artefacts to artefact repositories, creating releases in remote version control systems, and writing changelogs.
In Python Semantic Release <8.0.0, the publish
command was a one-stop-shop for
performing every piece of automation provided. This has been changed - the version
command now handles determining the next version, applying the changes to the project
metadata according to the configuration, writing a changelog, and committing/pushing
changes to the remote Git repository. It also handles creating a release in the remote
VCS. It does not publish software artefacts to remote repositories such as PyPI;
the rationale behind this decision is simply that under the hood, Python Semantic Release
used twine to upload artefacts to package indexes such as PyPI, and it’s recommended
to use twine directly via the command-line. From the twine
documentation:
Twine is a command-line tool for interacting with PyPI securely over HTTPS.
As a result Python Semantic Release no longer depends on twine internals.
The publish
command now handles publishing software artefacts to releases in the
remote version control system.
To achieve a similar flow of logic such as
Determine the next version
Write this version to the configured metadata locations
Write the changelog
Push the changes to the metadata and changelog to the remote repository
Create a release in the remote version control system
Build a wheel
Publish the wheel to PyPI
Publish the distribution artifacts to the release in the remote VCS
You should run:
semantic-release version
twine upload dist/* # or whichever path your distributions are placed in
semantic-release publish
With steps 1-6 being handled by the semantic-release version command, step 7 being left to the developer to handle, and lastly step 8 to be handled by the semantic-release publish command.
Removal of -D/--define
command-line option¶
It is no longer possible to override arbitrary configuration values using the -D
/
--define
option. You should provide the appropriate values via a configuration
file using -c/--config [FILE] or via the available command-line options.
This simplifies the command-line option parsing significantly and is less error-prone, which has resulted in previous issues (e.g. #600) with overrides on the command-line. Some of the configuration values expected by Python Semantic Release use complex data types such as lists or nested structures, which would be tedious and error-prone to specify using just command-line options.
Removal of CI verifications¶
Prior to v8, Python Semantic Release would perform some prerequisite verification
of environment variables before performing any version changes using the publish
command. It’s not feasible for Python Semantic Release to verify any possible CI
environment fully, and these checks were only triggered if certain environment
variables were set - they wouldn’t fail locally.
These checks previously raised :py:class:semantic_release.CiVerificationError
, and
were the only place in which this custom exception was used. Therefore, this exception
has also been removed from Python Semantic Release in v8.
If you were relying on this functionality, it’s recommended that you add the following
shell commands before invoking semantic-release
to verify your environment:
Note
In the following, $RELEASE_BRANCH refers to the git branch against which you run your
releases using Python Semantic Release. You will need to ensure it is set properly
(e.g. via export RELEASE_BRANCH=main
and/or replace the variable with the branch
name you want to verify the CI environment for.
Travis¶
Condition: environment variable TRAVIS=true
Replacement:
if ! [[
$TRAVIS_BRANCH == $RELEASE_BRANCH && \
$TRAVIS_PULL_REQUEST == 'false'
]]; then
exit 1
fi
Semaphore¶
Condition: environment variable SEMAPHORE=true
Replacement:
if ! [[
$BRANCH_NAME == $RELEASE_BRANCH && \
$SEMAPHORE_THREAD_RESULT != 'failed' && \
-n $PULL_REQUEST_NUMBER
]]; then
exit 1
fi
Frigg¶
Condition: environment variable FRIGG=true
Replacement:
if ! [[
$FRIGG_BUILD_BRANCH == $RELEASE_BRANCH && \
-n $FRIGG_PULL_REQUEST
]]; then
exit 1
fi
Circle CI¶
Condition: environment variable CIRCLECI=true
Replacement:
if ! [[
$CIRCLE_BRANCH == $RELEASE_BRANCH && \
-n $CI_PULL_REQUEST
]]; then
exit 1
fi
GitLab CI¶
Condition: environment variable GITLAB_CI=true
Replacement:
if ! [[ $CI_COMMIT_REF_NAME == $RELEASE_BRANCH ]]; then
exit 1
fi
Condition: environment variable BITBUCKET_BUILD_NUMBER
is set
Replacement:
if ! [[
$BITBUCKET_BRANCH == $RELEASE_BRANCH && \
-n $BITBUCKET_PR_ID
]]; then
exit 1
fi
Jenkins¶
Condition: environment variable JENKINS_URL
is set
Replacement:
if [[ -z $BRANCH_NAME ]]; then
BRANCH_NAME=$BRANCH_NAME
elif [[ -z $GIT_BRANCH ]]; then
BRANCH_NAME=$GIT_BRANCH
fi
if ! [[
$BRANCH_NAME == $RELEASE_BRANCH && \
-n $CHANGE_ID
]]; then
exit 1
fi
Removal of Build Status Checking¶
Prior to v8, Python Semantic Release contained a configuration option,
check_build_status
, which would attempt to prevent a release being made
if it was possible to identify that a corresponding build pipeline was failing.
For similar reasons to those motivating the removal of
CI Checks, this feature has also been removed.
If you are leveraging this feature in Python Semantic Release v7, the following
bash commands will replace the functionality, and you can add these to your pipeline.
You will need to install jq
and curl
to run these commands; they can be easily
installed through your system’s package manager, for example on Ubuntu:
sudo apt update && sudo apt upgrade
sudo apt install -y curl jq
On Windows, you can refer to the installation guide for jq, and if curl
is not already
installed, you can download it from the curl website
GitHub¶
export RESP="$(
curl \
-H "Authorization: token $GITHUB_TOKEN" \
-fSsL https://$GITHUB_API_DOMAIN/repos/$REPO_OWNER/$REPO_NAME/commits/$(git rev-parse HEAD)/status || exit 1
)"
if [ $(jq -r '.state' <<< "$RESP") != "success" ]; then
echo "Build status is not success" >&2
exit 1
fi
Note that $GITHUB_API_DOMAIN
is typically api.github.com
unless you are using
GitHub Enterprise with a custom domain name.
Gitea¶
export RESP="$(
curl \
-H "Authorization: token $GITEA_TOKEN" \
-fSsL https://$GITEA_DOMAIN/repos/$REPO_OWNER/$REPO_NAME/statuses/$(git rev-parse HEAD) || exit 1
)"
if [ $(jq -r '.state' <<< "$RESP") != "success" ]; then
echo "Build status is not success" >&2
exit 1
fi
Gitlab¶
export RESP="$(
curl \
-H "Authorization: token $GITLAB_TOKEN" \
-fSsL https://$GITLAB_DOMAIN/api/v4/projects/$PROJECT_ID/repository/commits/$(git rev-parse HEAD)/statuses
)"
for line in $(jq -r '.[] | [.name, .status, .allow_failure] | join("|")' <<<"$RESP"); do
IFS="|" read -r job_name job_status allow_failure <<<"$line"
if [ "$job_status" == "pending" ]; then
echo "job $job_name is pending" >&2
exit 1
elif [ "$job_status" == "failed" ] && [ ! "$allow_failure" == "true" ]; then
echo "job $job_name failed" >&2
exit 1
fi
done
Multibranch releases¶
Prior to v8, Python Semantic Release would perform git checkout
to switch to your
configured release branch and determine if a release would need to be made. In v8 this
has been changed - you must manually check out the branch which you would like to release
against, and if you would like to create releases against this branch you must also ensure
that it belongs to a release group.
changelog
command¶
A new option, --post-to-release-tag [TAG] has been added. If you
omit this argument on the command line then the changelog rendering process, which is
described in more detail at Template Rendering, will be
triggered, but the new changelog will not be posted to any release.
If you use this new command-line option, it should be set to a tag within the remote
which has a corresponding release.
For example, to update the changelog and post it to the release corresponding to the
tag v1.1.4
, you should run:
semantic-release changelog --post-to-release-tag v1.1.4
Changelog customisation¶
A number of options relevant to customising the changelog have been removed. This is because Python Semantic Release now supports authoring a completely custom Jinja template with the contents of your changelog. Historically, the number of options added to Python Semantic Release in order to allow this customisation has grown significantly; it now uses templates in order to fully open up customising the changelog’s appearance.
Configuration¶
The configuration structure has been completely reworked, so you should read Configuration carefully during the process of upgrading to v8+. However, some common pitfalls and potential sources of confusion are summarised here.
setup.cfg
is no longer supported¶
Python Semantic Release no longer supports configuration via setup.cfg
. This is
because the Python ecosystem is centering around pyproject.toml
as universal tool
and project configuration file, and TOML allows expressions via configuration, such as
the mechanism for declaring configuration via environment variables, which introduce
much greater complexity to support in the otherwise equivalent ini
-format
configuration.
You can use semantic-release generate-config to generate new-format configuration that can
be added to pyproject.toml
, and adjust the default settings according to your
needs.
Warning
If you don’t already have a pyproject.toml
configuration file, pip
can
change its behaviour once you add one, as a result of PEP-517. If you find
that this breaks your packaging, you can add your Python Semantic Release
configuration to a separate file such as semantic-release.toml
, and use
the –config option to reference this alternative
configuration file.
More detail about this issue can be found in this pip issue.
Commit parser options¶
Options such as major_emoji
, parser_angular_patch_types
or
parser_angular_default_level_bump
have been removed. Instead, these have been
replaced with a single set of recognised commit parser options, allowed_tags
,
major_tags
, minor_tags
, and patch_tags
, though the interpretation of
these is up to the specific parsers in use. You can read more detail about using
commit parser options in commit_parser_options,
and if you need to parse multiple commit styles for a single project it’s recommended
that you create a parser following Writing your own parser that
is tailored to the specific needs of your project.
version_variable
¶
This option has been renamed to version_variables as it refers to a list of variables which can be updated.
version_pattern
¶
This option has been removed. It’s recommended to use an alternative tool to perform
substitution using arbitrary regular expressions, such as sed
.
You can always use Python Semantic Release to identify the next version to be created
for a project and store this in an environment variable like so:
export VERSION=$(semantic-release version --print)
version_toml
¶
This option will no longer accept a string or comma-separated string of version
locations to be updated in TOML files. Instead, you must supply a List[str]
.
For existing configurations using a single location in this option, you can
simply wrap the value in []
:
# Python Semantic Release v7 configuration
[tool.semantic_release]
version_toml = "pyproject.toml:tool.poetry.version"
# Python Semantic Release v8 configuration
[tool.semantic_release]
version_toml = ["pyproject.toml:tool.poetry.version"]
tag_format
¶
This option has the same effect as it did in Python Semantic Release prior to v8,
but Python Semantic Release will now verify that it has a {version}
format
key and raise an error if this is not the case.
upload_to_release
¶
This option has been renamed to upload_to_vcs_release.
Custom Commit Parsers¶
Previously, a custom commit parser had to satisfy the following criteria:
It should be
import
-able from the virtual environment where thesemantic-release
is runIt should be a function which accepts the commit message as its only argument and returns a
semantic_release.history.parser_helpers.ParsedCommit
if the commit is parsed successfully, or raise asemantic_release.UnknownCommitMessageStyleError
if parsing is unsuccessful.
It is still possible to implement custom commit parsers, but the interface for doing so has been modified with stronger support for Python type annotations and broader input provided to the parser to enable capturing more information from each commit, such as the commit’s date and author, if desired. A full guide to implementing a custom commit parser can be found at Writing your own parser.
semantic_release¶
semantic_release package¶
Python Semantic Release
- semantic_release.setup_hook(argv: list[str]) None [source]¶
A hook to be used in setup.py to enable python setup.py publish.
- Parameters:
argv – sys.argv
Subpackages¶
semantic_release.changelog package¶
Submodules¶
semantic_release.changelog.context module¶
- class semantic_release.changelog.context.ChangelogContext(repo_name: 'str', repo_owner: 'str', hvcs_type: 'str', history: 'ReleaseHistory', filters: 'tuple[Callable[..., Any], ...]' = ())[source]¶
Bases:
object
- filters: tuple[Callable[..., Any], ...] = ()¶
- history: ReleaseHistory¶
- hvcs_type: str¶
- repo_name: str¶
- repo_owner: str¶
- semantic_release.changelog.context.make_changelog_context(hvcs_client: HvcsBase, release_history: ReleaseHistory) ChangelogContext [source]¶
semantic_release.changelog.release_history module¶
- class semantic_release.changelog.release_history.Release[source]¶
Bases:
TypedDict
- committer: Actor¶
- elements: dict[str, list[ParseResult]]¶
- tagged_date: datetime¶
- tagger: Actor¶
- class semantic_release.changelog.release_history.ReleaseHistory(unreleased: dict[str, list[ParseResult]], released: dict[Version, Release])[source]¶
Bases:
object
- classmethod from_git_history(repo: Repo, translator: VersionTranslator, commit_parser: CommitParser[ParseResult, ParserOptions], exclude_commit_patterns: Iterable[Pattern[str]] = ()) ReleaseHistory [source]¶
- release(version: Version, tagger: Actor, committer: Actor, tagged_date: datetime) ReleaseHistory [source]¶
semantic_release.changelog.template module¶
- semantic_release.changelog.template.environment(template_dir: Path | str = '.', block_start_string: str = '{%', block_end_string: str = '%}', variable_start_string: str = '{{', variable_end_string: str = '}}', comment_start_string: str = '{#', comment_end_string: str = '#}', line_statement_prefix: str | None = None, line_comment_prefix: str | None = None, trim_blocks: bool = False, lstrip_blocks: bool = False, newline_sequence: Literal['\n', '\r', '\r\n'] = '\n', keep_trailing_newline: bool = False, extensions: Iterable[str] = (), autoescape: bool | str = True) SandboxedEnvironment [source]¶
Create a jinja2.sandbox.SandboxedEnvironment with certain parameter resrictions.
For example the Loader is fixed to FileSystemLoader, although the searchpath is configurable.
autoescape
can be a string in which case it should follow the conventionmodule:attr
, in this instance it will be dynamically imported. See https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment for full parameter descriptions
semantic_release.cli package¶
Subpackages¶
semantic_release.cli.commands package¶
Submodules¶
semantic_release.cli.commands.changelog module¶
semantic_release.cli.commands.cli_context module¶
- class semantic_release.cli.commands.cli_context.CliContextObj(ctx: click.Context, logger: logging.Logger, global_opts: GlobalCommandLineOptions)[source]¶
Bases:
object
- property runtime_ctx: RuntimeContext¶
Lazy load the runtime context. This is done to avoid configuration loading when the command is not run. This is useful for commands like –help and –version
semantic_release.cli.commands.generate_config module¶
semantic_release.cli.commands.main module¶
semantic_release.cli.commands.publish module¶
semantic_release.cli.commands.version module¶
- semantic_release.cli.commands.version.apply_version_to_source_files(repo: Repo, version_declarations: Iterable[VersionDeclarationABC], version: Version, noop: bool = False) list[str] [source]¶
- semantic_release.cli.commands.version.is_forced_prerelease(as_prerelease: bool, forced_level_bump: LevelBump | None, prerelease: bool) bool [source]¶
Determine if this release is forced to have prerelease on/off. If
force_prerelease
is set then yes. Otherwise if we are forcing a specific level bump without force_prerelease, it’s False. Otherwise (force_level is None
) use the value ofprerelease
- semantic_release.cli.commands.version.last_released(repo: Repo, translator: VersionTranslator) tuple[Tag, Version] | None [source]¶
- semantic_release.cli.commands.version.shell(cmd: str, *, env: Mapping[str, str] | None = None, check: bool = True) subprocess.CompletedProcess [source]¶
- semantic_release.cli.commands.version.version_from_forced_level(repo: Repo, forced_level_bump: LevelBump, translator: VersionTranslator) Version [source]¶
Submodules¶
semantic_release.cli.common module¶
- semantic_release.cli.common.get_release_notes_template(template_dir: Path) str [source]¶
Read the project’s template for release notes, falling back to the default.
semantic_release.cli.config module¶
- class semantic_release.cli.config.BranchConfig(*, match: str = '(main|master)', prerelease_token: str = 'rc', prerelease: bool = False)[source]¶
Bases:
BaseModel
- match: str¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'match': FieldInfo(annotation=str, required=False, default='(main|master)'), 'prerelease': FieldInfo(annotation=bool, required=False, default=False), 'prerelease_token': FieldInfo(annotation=str, required=False, default='rc')}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- prerelease: bool¶
- prerelease_token: str¶
- class semantic_release.cli.config.ChangelogConfig(*, template_dir: str = 'templates', changelog_file: str = 'CHANGELOG.md', exclude_commit_patterns: Tuple[str, ...] = (), environment: ChangelogEnvironmentConfig = ChangelogEnvironmentConfig(block_start_string='{%', block_end_string='%}', variable_start_string='{{', variable_end_string='}}', comment_start_string='{#', comment_end_string='#}', line_statement_prefix=None, line_comment_prefix=None, trim_blocks=False, lstrip_blocks=False, newline_sequence='\n', keep_trailing_newline=False, extensions=(), autoescape=True))[source]¶
Bases:
BaseModel
- changelog_file: str¶
- environment: ChangelogEnvironmentConfig¶
- exclude_commit_patterns: Tuple[str, ...]¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'changelog_file': FieldInfo(annotation=str, required=False, default='CHANGELOG.md'), 'environment': FieldInfo(annotation=ChangelogEnvironmentConfig, required=False, default=ChangelogEnvironmentConfig(block_start_string='{%', block_end_string='%}', variable_start_string='{{', variable_end_string='}}', comment_start_string='{#', comment_end_string='#}', line_statement_prefix=None, line_comment_prefix=None, trim_blocks=False, lstrip_blocks=False, newline_sequence='\n', keep_trailing_newline=False, extensions=(), autoescape=True)), 'exclude_commit_patterns': FieldInfo(annotation=Tuple[str, ...], required=False, default=()), 'template_dir': FieldInfo(annotation=str, required=False, default='templates')}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- template_dir: str¶
- class semantic_release.cli.config.ChangelogEnvironmentConfig(*, block_start_string: str = '{%', block_end_string: str = '%}', variable_start_string: str = '{{', variable_end_string: str = '}}', comment_start_string: str = '{#', comment_end_string: str = '#}', line_statement_prefix: str | None = None, line_comment_prefix: str | None = None, trim_blocks: bool = False, lstrip_blocks: bool = False, newline_sequence: Literal['\n', '\r', '\r\n'] = '\n', keep_trailing_newline: bool = False, extensions: Tuple[str, ...] = (), autoescape: bool | str = True)[source]¶
Bases:
BaseModel
- autoescape: bool | str¶
- block_end_string: str¶
- block_start_string: str¶
- comment_end_string: str¶
- comment_start_string: str¶
- extensions: Tuple[str, ...]¶
- keep_trailing_newline: bool¶
- line_comment_prefix: str | None¶
- line_statement_prefix: str | None¶
- lstrip_blocks: bool¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'autoescape': FieldInfo(annotation=Union[bool, str], required=False, default=True), 'block_end_string': FieldInfo(annotation=str, required=False, default='%}'), 'block_start_string': FieldInfo(annotation=str, required=False, default='{%'), 'comment_end_string': FieldInfo(annotation=str, required=False, default='#}'), 'comment_start_string': FieldInfo(annotation=str, required=False, default='{#'), 'extensions': FieldInfo(annotation=Tuple[str, ...], required=False, default=()), 'keep_trailing_newline': FieldInfo(annotation=bool, required=False, default=False), 'line_comment_prefix': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'line_statement_prefix': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'lstrip_blocks': FieldInfo(annotation=bool, required=False, default=False), 'newline_sequence': FieldInfo(annotation=Literal['\n', '\r', '\r\n'], required=False, default='\n'), 'trim_blocks': FieldInfo(annotation=bool, required=False, default=False), 'variable_end_string': FieldInfo(annotation=str, required=False, default='}}'), 'variable_start_string': FieldInfo(annotation=str, required=False, default='{{')}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- newline_sequence: Literal['\n', '\r', '\r\n']¶
- trim_blocks: bool¶
- variable_end_string: str¶
- variable_start_string: str¶
- class semantic_release.cli.config.EnvConfigVar(*, env: str, default: str | None = None, default_env: str | None = None)[source]¶
Bases:
BaseModel
- default: str | None¶
- default_env: str | None¶
- env: str¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'default': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'default_env': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'env': FieldInfo(annotation=str, required=True)}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- class semantic_release.cli.config.GlobalCommandLineOptions(noop: bool = False, verbosity: int = 0, config_file: str = 'pyproject.toml', strict: bool = False)[source]¶
Bases:
object
A dataclass to hold all the command line options that should be set in the RuntimeContext
- config_file: str = 'pyproject.toml'¶
- noop: bool = False¶
- strict: bool = False¶
- verbosity: int = 0¶
- class semantic_release.cli.config.HvcsClient(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
str
,Enum
- BITBUCKET = 'bitbucket'¶
- GITEA = 'gitea'¶
- GITHUB = 'github'¶
- GITLAB = 'gitlab'¶
- class semantic_release.cli.config.PublishConfig(*, dist_glob_patterns: Tuple[str, ...] = ('dist/*',), upload_to_vcs_release: bool = True)[source]¶
Bases:
BaseModel
- dist_glob_patterns: Tuple[str, ...]¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'dist_glob_patterns': FieldInfo(annotation=Tuple[str, ...], required=False, default=('dist/*',)), 'upload_to_vcs_release': FieldInfo(annotation=bool, required=False, default=True)}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- upload_to_vcs_release: bool¶
- class semantic_release.cli.config.RawConfig(*, assets: ~typing.List[str] = [], branches: ~typing.Dict[str, ~semantic_release.cli.config.BranchConfig] = {'main': BranchConfig(match='(main|master)', prerelease_token='rc', prerelease=False)}, build_command: str | None = None, build_command_env: ~typing.List[str] = [], changelog: ~semantic_release.cli.config.ChangelogConfig = ChangelogConfig(template_dir='templates', changelog_file='CHANGELOG.md', exclude_commit_patterns=(), environment=ChangelogEnvironmentConfig(block_start_string='{%', block_end_string='%}', variable_start_string='{{', variable_end_string='}}', comment_start_string='{#', comment_end_string='#}', line_statement_prefix=None, line_comment_prefix=None, trim_blocks=False, lstrip_blocks=False, newline_sequence='\n', keep_trailing_newline=False, extensions=(), autoescape=True)), commit_author: ~semantic_release.cli.config.EnvConfigVar | str = EnvConfigVar(env='GIT_COMMIT_AUTHOR', default='semantic-release <semantic-release>', default_env=None), commit_message: str = '{version}\n\nAutomatically generated by python-semantic-release', commit_parser: str = 'angular', commit_parser_options: ~typing.Dict[str, ~typing.Any] = {}, logging_use_named_masks: bool = False, major_on_zero: bool = True, allow_zero_version: bool = True, remote: ~semantic_release.cli.config.RemoteConfig = RemoteConfig(name='origin', token=None, url=None, type=<HvcsClient.GITHUB: 'github'>, domain=None, api_domain=None, ignore_token_for_push=False, insecure=False), tag_format: str = 'v{version}', publish: ~semantic_release.cli.config.PublishConfig = PublishConfig(dist_glob_patterns=('dist/*', ), upload_to_vcs_release=True), version_toml: ~typing.Tuple[str, ...] | None = None, version_variables: ~typing.Tuple[str, ...] | None = None)[source]¶
Bases:
BaseModel
- allow_zero_version: bool¶
- assets: List[str]¶
- branches: Dict[str, BranchConfig]¶
- build_command: str | None¶
- build_command_env: List[str]¶
- changelog: ChangelogConfig¶
- commit_author: MaybeFromEnv¶
- commit_message: str¶
- commit_parser: NonEmptyString¶
- commit_parser_options: Dict[str, Any]¶
- logging_use_named_masks: bool¶
- major_on_zero: bool¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'allow_zero_version': FieldInfo(annotation=bool, required=False, default=True), 'assets': FieldInfo(annotation=List[str], required=False, default=[]), 'branches': FieldInfo(annotation=Dict[str, BranchConfig], required=False, default={'main': BranchConfig(match='(main|master)', prerelease_token='rc', prerelease=False)}), 'build_command': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'build_command_env': FieldInfo(annotation=List[str], required=False, default=[]), 'changelog': FieldInfo(annotation=ChangelogConfig, required=False, default=ChangelogConfig(template_dir='templates', changelog_file='CHANGELOG.md', exclude_commit_patterns=(), environment=ChangelogEnvironmentConfig(block_start_string='{%', block_end_string='%}', variable_start_string='{{', variable_end_string='}}', comment_start_string='{#', comment_end_string='#}', line_statement_prefix=None, line_comment_prefix=None, trim_blocks=False, lstrip_blocks=False, newline_sequence='\n', keep_trailing_newline=False, extensions=(), autoescape=True))), 'commit_author': FieldInfo(annotation=Union[EnvConfigVar, str], required=False, default=EnvConfigVar(env='GIT_COMMIT_AUTHOR', default='semantic-release <semantic-release>', default_env=None)), 'commit_message': FieldInfo(annotation=str, required=False, default='{version}\n\nAutomatically generated by python-semantic-release'), 'commit_parser': FieldInfo(annotation=str, required=False, default='angular', metadata=[MinLen(min_length=1)]), 'commit_parser_options': FieldInfo(annotation=Dict[str, Any], required=False, default={}), 'logging_use_named_masks': FieldInfo(annotation=bool, required=False, default=False), 'major_on_zero': FieldInfo(annotation=bool, required=False, default=True), 'publish': FieldInfo(annotation=PublishConfig, required=False, default=PublishConfig(dist_glob_patterns=('dist/*',), upload_to_vcs_release=True)), 'remote': FieldInfo(annotation=RemoteConfig, required=False, default=RemoteConfig(name='origin', token=None, url=None, type=<HvcsClient.GITHUB: 'github'>, domain=None, api_domain=None, ignore_token_for_push=False, insecure=False)), 'tag_format': FieldInfo(annotation=str, required=False, default='v{version}'), 'version_toml': FieldInfo(annotation=Union[Tuple[str, ...], NoneType], required=False, default=None), 'version_variables': FieldInfo(annotation=Union[Tuple[str, ...], NoneType], required=False, default=None)}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- publish: PublishConfig¶
- remote: RemoteConfig¶
- tag_format: str¶
- version_toml: Tuple[str, ...] | None¶
- version_variables: Tuple[str, ...] | None¶
- class semantic_release.cli.config.RemoteConfig(*, name: str = 'origin', token: str | None = None, url: str | None = None, type: HvcsClient = HvcsClient.GITHUB, domain: str | None = None, api_domain: str | None = None, ignore_token_for_push: bool = False, insecure: bool = False)[source]¶
Bases:
BaseModel
- api_domain: str | None¶
- domain: str | None¶
- ignore_token_for_push: bool¶
- insecure: bool¶
- model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] = {}¶
A dictionary of computed field names and their corresponding ComputedFieldInfo objects.
- model_config: ClassVar[ConfigDict] = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_fields: ClassVar[dict[str, FieldInfo]] = {'api_domain': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'domain': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'ignore_token_for_push': FieldInfo(annotation=bool, required=False, default=False), 'insecure': FieldInfo(annotation=bool, required=False, default=False), 'name': FieldInfo(annotation=str, required=False, default='origin'), 'token': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=HvcsClient, required=False, default=<HvcsClient.GITHUB: 'github'>), 'url': FieldInfo(annotation=Union[str, NoneType], required=False, default=None)}¶
Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo].
This replaces Model.__fields__ from Pydantic V1.
- name: str¶
- token: str | None¶
- type: HvcsClient¶
- url: str | None¶
- class semantic_release.cli.config.RuntimeContext(repo: 'Repo', commit_parser: 'CommitParser[ParseResult, ParserOptions]', version_translator: 'VersionTranslator', major_on_zero: 'bool', allow_zero_version: 'bool', prerelease: 'bool', assets: 'List[str]', commit_author: 'Actor', commit_message: 'str', changelog_excluded_commit_patterns: 'Tuple[re.Pattern[str], ...]', version_declarations: 'Tuple[VersionDeclarationABC, ...]', hvcs_client: 'hvcs.HvcsBase', changelog_file: 'Path', ignore_token_for_push: 'bool', template_environment: 'Environment', template_dir: 'Path', build_command: 'Optional[str]', build_command_env: 'dict[str, str]', dist_glob_patterns: 'Tuple[str, ...]', upload_to_vcs_release: 'bool', global_cli_options: 'GlobalCommandLineOptions', masker: 'MaskingFilter')[source]¶
Bases:
object
- allow_zero_version: bool¶
- apply_log_masking(masker: MaskingFilter) MaskingFilter [source]¶
- assets: List[str]¶
- build_command: str | None¶
- build_command_env: dict[str, str]¶
- changelog_excluded_commit_patterns: Tuple[Pattern[str], ...]¶
- changelog_file: Path¶
- commit_author: Actor¶
- commit_message: str¶
- commit_parser: CommitParser[ParsedCommit | ParseError, ParserOptions]¶
- dist_glob_patterns: Tuple[str, ...]¶
- classmethod from_raw_config(raw: RawConfig, global_cli_options: GlobalCommandLineOptions) RuntimeContext [source]¶
- global_cli_options: GlobalCommandLineOptions¶
- ignore_token_for_push: bool¶
- major_on_zero: bool¶
- masker: MaskingFilter¶
- prerelease: bool¶
- repo: Repo¶
- static resolve_from_env(param: EnvConfigVar | str | None) str | None [source]¶
- static select_branch_options(choices: Dict[str, BranchConfig], active_branch: str) BranchConfig [source]¶
- template_dir: Path¶
- template_environment: Environment¶
- upload_to_vcs_release: bool¶
- version_declarations: Tuple[VersionDeclarationABC, ...]¶
- version_translator: VersionTranslator¶
semantic_release.cli.const module¶
semantic_release.cli.github_actions_output module¶
semantic_release.cli.masking_filter module¶
- class semantic_release.cli.masking_filter.MaskingFilter(_use_named_masks: bool = False, **patterns: Iterable[str | Pattern[str]])[source]¶
Bases:
Filter
- REPLACE_STR = '****'¶
- add_mask_for(data: str, name: str = 'redacted') MaskingFilter [source]¶
semantic_release.cli.util module¶
Utilities for command-line functionality
- semantic_release.cli.util.indented(msg: str, prefix: str = ' ') str [source]¶
Convenience function for text-formatting for the console.
Ensures the least indented line of the msg string is indented by
prefix
with consistent alignment of the remainder ofmsg
irrespective of the level of indentation in the Python source code
- semantic_release.cli.util.load_raw_config_file(config_file: Path | str) dict[Any, Any] [source]¶
Load raw configuration as a dict from the filename specified by config_filename, trying the following parsing methods:
try to parse with tomli.load (guessing it’s a TOML file)
try to parse with json.load (guessing it’s a JSON file)
raise InvalidConfiguration if none of the above parsing methods work
This function will also raise FileNotFoundError if it is raised while trying to read the specified configuration file
- semantic_release.cli.util.noop_report(msg: str) None [source]¶
Rich-prints a msg with a standard prefix to report when an action is not being taken due to a “noop” flag
semantic_release.commit_parser package¶
Submodules¶
semantic_release.commit_parser.angular module¶
Angular commit style parser https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines
- class semantic_release.commit_parser.angular.AngularCommitParser(options: AngularParserOptions | None = None)[source]¶
Bases:
CommitParser
[Union
[ParsedCommit
,ParseError
],AngularParserOptions
]A commit parser for projects conforming to the angular style of conventional commits. See https://www.conventionalcommits.org/en/v1.0.0-beta.4/
- static get_default_options() AngularParserOptions [source]¶
- parse(commit: Commit) ParseResult [source]¶
Attempt to parse the commit message with a regular expression into a ParseResult
- parser_options¶
alias of
AngularParserOptions
- class semantic_release.commit_parser.angular.AngularParserOptions(allowed_tags: Tuple[str, ...] = ('build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'style', 'refactor', 'test'), minor_tags: Tuple[str, ...] = ('feat',), patch_tags: Tuple[str, ...] = ('fix', 'perf'), default_bump_level: LevelBump = LevelBump.NO_RELEASE)[source]¶
Bases:
ParserOptions
Options dataclass for AngularCommitParser
- allowed_tags: Tuple[str, ...] = ('build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'style', 'refactor', 'test')¶
- minor_tags: Tuple[str, ...] = ('feat',)¶
- patch_tags: Tuple[str, ...] = ('fix', 'perf')¶
semantic_release.commit_parser.emoji module¶
Commit parser which looks for emojis to determine the type of commit
- class semantic_release.commit_parser.emoji.EmojiCommitParser(options: _OPTS | None = None)[source]¶
Bases:
CommitParser
[Union
[ParsedCommit
,ParseError
],EmojiParserOptions
]Parse a commit using an emoji in the subject line. When multiple emojis are encountered, the one with the highest bump level is used. If there are multiple emojis on the same level, the we use the one listed earliest in the configuration. If the message does not contain any known emojis, then the level to bump will be 0 and the type of change “Other”. This parser never raises UnknownCommitMessageStyleError. Emojis are not removed from the description, and will appear alongside the commit subject in the changelog.
- static get_default_options() EmojiParserOptions [source]¶
- parse(commit: Commit) ParsedCommit | ParseError [source]¶
- parser_options¶
alias of
EmojiParserOptions
- class semantic_release.commit_parser.emoji.EmojiParserOptions(major_tags: Tuple[str, ...] = (':boom:',), minor_tags: Tuple[str, ...] = (':sparkles:', ':children_crossing:', ':lipstick:', ':iphone:', ':egg:', ':chart_with_upwards_trend:'), patch_tags: Tuple[str, ...] = (':ambulance:', ':lock:', ':bug:', ':zap:', ':goal_net:', ':alien:', ':wheelchair:', ':speech_balloon:', ':mag:', ':apple:', ':penguin:', ':checkered_flag:', ':robot:', ':green_apple:'), default_bump_level: LevelBump = LevelBump.NO_RELEASE)[source]¶
Bases:
ParserOptions
- major_tags: Tuple[str, ...] = (':boom:',)¶
- minor_tags: Tuple[str, ...] = (':sparkles:', ':children_crossing:', ':lipstick:', ':iphone:', ':egg:', ':chart_with_upwards_trend:')¶
- patch_tags: Tuple[str, ...] = (':ambulance:', ':lock:', ':bug:', ':zap:', ':goal_net:', ':alien:', ':wheelchair:', ':speech_balloon:', ':mag:', ':apple:', ':penguin:', ':checkered_flag:', ':robot:', ':green_apple:')¶
semantic_release.commit_parser.scipy module¶
Parses commit messages using scipy tags of the form:
<tag>(<scope>): <subject>
<body>
The elements <tag>, <scope> and <body> are optional. If no tag is present, the commit will be added to the changelog section “None” and no version increment will be performed.
While <scope> is supported here it isn’t actually part of the scipy style. If it is missing, parentheses around it are too. The commit should then be of the form:
<tag>: <subject>
<body>
To communicate a breaking change add “BREAKING CHANGE” into the body at the beginning of a paragraph. Fill this paragraph with information how to migrate from the broken behavior to the new behavior. It will be added to the “Breaking” section of the changelog.
Supported Tags:
API, DEP, ENH, REV, BUG, MAINT, BENCH, BLD,
DEV, DOC, STY, TST, REL, FEAT, TEST
Supported Changelog Sections:
breaking, feature, fix, Other, None
- class semantic_release.commit_parser.scipy.ScipyCommitParser(options: ScipyParserOptions | None = None)[source]¶
Bases:
CommitParser
[Union
[ParsedCommit
,ParseError
],ScipyParserOptions
]Parser for scipy-style commit messages
- static get_default_options() ScipyParserOptions [source]¶
- parser_options¶
alias of
ScipyParserOptions
- class semantic_release.commit_parser.scipy.ScipyParserOptions(major_tags: Tuple[str, ...] = ('API',), minor_tags: Tuple[str, ...] = ('DEP', 'DEV', 'ENH', 'REV', 'FEAT'), patch_tags: Tuple[str, ...] = ('BLD', 'BUG', 'MAINT'), allowed_tags: Tuple[str, ...] = ('API', 'DEP', 'DEV', 'ENH', 'REV', 'FEAT', 'BLD', 'BUG', 'MAINT', 'BENCH', 'DOC', 'STY', 'TST', 'REL', 'TEST'), default_level_bump: LevelBump = LevelBump.NO_RELEASE)[source]¶
Bases:
ParserOptions
- allowed_tags: Tuple[str, ...] = ('API', 'DEP', 'DEV', 'ENH', 'REV', 'FEAT', 'BLD', 'BUG', 'MAINT', 'BENCH', 'DOC', 'STY', 'TST', 'REL', 'TEST')¶
- major_tags: Tuple[str, ...] = ('API',)¶
- minor_tags: Tuple[str, ...] = ('DEP', 'DEV', 'ENH', 'REV', 'FEAT')¶
- patch_tags: Tuple[str, ...] = ('BLD', 'BUG', 'MAINT')¶
semantic_release.commit_parser.tag module¶
Legacy commit parser from Python Semantic Release 1.0
- class semantic_release.commit_parser.tag.TagCommitParser(options: _OPTS | None = None)[source]¶
Bases:
CommitParser
[Union
[ParsedCommit
,ParseError
],TagParserOptions
]Parse a commit message according to the 1.0 version of python-semantic-release. It expects a tag of some sort in the commit message and will use the rest of the first line as changelog content.
- static get_default_options() TagParserOptions [source]¶
- parse(commit: Commit) ParsedCommit | ParseError [source]¶
- parser_options¶
alias of
TagParserOptions
semantic_release.commit_parser.token module¶
- class semantic_release.commit_parser.token.ParseError(commit, error)[source]¶
Bases:
NamedTuple
- commit: Commit¶
Alias for field number 0
- error: str¶
Alias for field number 1
- property hexsha: str¶
- property message: str¶
- property short_hash: str¶
- class semantic_release.commit_parser.token.ParsedCommit(bump, type, scope, descriptions, breaking_descriptions, commit)[source]¶
Bases:
NamedTuple
- breaking_descriptions: list[str]¶
Alias for field number 4
- commit: Commit¶
Alias for field number 5
- descriptions: list[str]¶
Alias for field number 3
- property hexsha: str¶
- property message: str¶
- scope: str¶
Alias for field number 2
- property short_hash: str¶
- type: str¶
Alias for field number 1
semantic_release.commit_parser.util module¶
- semantic_release.commit_parser.util.parse_paragraphs(text: str) list[str] [source]¶
This will take a text block and return a list containing each paragraph with single line breaks collapsed into spaces.
To handle Windows line endings, carriage returns ‘r’ are removed before separating into paragraphs.
- Parameters:
text – The text string to be divided.
- Returns:
A list of condensed paragraphs, as strings.
semantic_release.hvcs package¶
- class semantic_release.hvcs.Bitbucket(remote_url: str, *, hvcs_domain: str | None = None, hvcs_api_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Bitbucket HVCS interface for interacting with BitBucket repositories
This class supports the following products:
BitBucket Cloud
BitBucket Data Center Server (on-premises installations)
This interface does its best to detect which product is configured based on the provided domain. If it is the official bitbucket.org, the default domain, then it is considered as BitBucket Cloud which uses the subdomain api.bitbucket.org/2.0 for api communication.
If the provided domain is anything else, than it is assumed to be communicating with an on-premise or 3rd-party maintained BitBucket instance which matches with the BitBucket Data Center Server product. The on-prem server product uses a path prefix for handling api requests which is configured to be server.domain/rest/api/1.0 based on the documentation in April 2024.
- DEFAULT_API_PATH_CLOUD = '/2.0'¶
- DEFAULT_API_PATH_ONPREM = '/rest/api/1.0'¶
- DEFAULT_API_SUBDOMAIN_PREFIX = 'api'¶
- DEFAULT_API_URL_CLOUD = 'https://api.bitbucket.org/2.0'¶
- DEFAULT_DOMAIN = 'bitbucket.org'¶
- DEFAULT_ENV_TOKEN_NAME = 'BITBUCKET_TOKEN'¶
- compare_url(from_rev: str, to_rev: str) str [source]¶
Get the Bitbucket comparison link between two version tags. :param from_rev: The older version to compare. :param to_rev: The newer version to compare. :return: Link to view a comparison between the two versions.
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int | str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int | str [source]¶
Create a release in a remote VCS, if supported
Which includes uploading any assets as part of the release
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- class semantic_release.hvcs.Gitea(remote_url: str, *, hvcs_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Gitea helper class
- DEFAULT_API_PATH = '/api/v1'¶
- DEFAULT_DOMAIN = 'gitea.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GITEA_TOKEN'¶
- asset_upload_url(release_id: str) str [source]¶
Get the correct upload url for a release https://gitea.com/api/swagger#/repository/repoCreateReleaseAttachment :param release_id: ID of the release to upload to
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int [source]¶
Post release changelog :param version: The version number :param changelog: The release notes for this version :return: The status of the request
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int [source]¶
Create a new release
Ref: https://gitea.com/api/swagger#/repository/repoCreateRelease
- Parameters:
tag – Tag to create release for
release_notes – The release notes for this version
prerelease – Whether or not this release should be specified as a
prerelease
- Returns:
Whether the request succeeded
- edit_release_notes(release_id: int, release_notes: str) int [source]¶
Edit a release with updated change notes https://gitea.com/api/swagger#/repository/repoEditRelease :param id: ID of release to update :param release_notes: The release notes for this version :return: The ID of the release that was edited
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- get_release_id_by_tag(tag: str) int | None [source]¶
Get a release by its tag name https://gitea.com/api/swagger#/repository/repoGetReleaseByTag :param tag: Tag to get release for :return: ID of found release
- remote_url(use_token: bool = True) str [source]¶
Get the remote url including the token for authentication if requested
- upload_dists(tag: str, dist_glob: str) int [source]¶
Upload distributions to a release :param tag: Tag to upload for :param path: Path to the dist directory :return: The number of distributions successfully uploaded
- upload_release_asset(release_id: int, file: str, label: str | None = None) bool [source]¶
Upload an asset to an existing release https://gitea.com/api/swagger#/repository/repoCreateReleaseAttachment :param release_id: ID of the release to upload to :param file: Path of the file to upload :param label: this parameter has no effect :return: The status of the request
- class semantic_release.hvcs.Github(remote_url: str, *, hvcs_domain: str | None = None, hvcs_api_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
GitHub HVCS interface for interacting with GitHub repositories
This class supports the following products:
GitHub Free, Pro, & Team
GitHub Enterprise Cloud
GitHub Enterprise Server (on-premises installations)
This interface does its best to detect which product is configured based on the provided domain. If it is the official github.com, the default domain, then it is considered as GitHub Enterprise Cloud which uses the subdomain api.github.com for api communication.
If the provided domain is anything else, than it is assumed to be communicating with an on-premise or 3rd-party maintained GitHub instance which matches with the GitHub Enterprise Server product. The on-prem server product uses a path prefix for handling api requests which is configured to be server.domain/api/v3 based on the documentation in April 2024.
- DEFAULT_API_DOMAIN = 'api.github.com'¶
- DEFAULT_API_PATH_CLOUD = '/'¶
- DEFAULT_API_PATH_ONPREM = '/api/v3'¶
- DEFAULT_API_SUBDOMAIN_PREFIX = 'api'¶
- DEFAULT_API_URL_CLOUD = 'https://api.github.com'¶
- DEFAULT_DOMAIN = 'github.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GH_TOKEN'¶
- asset_upload_url(release_id: str) str | None [source]¶
Get the correct upload url for a release https://docs.github.com/en/enterprise-server@3.5/rest/releases/releases#get-a-release :param release_id: ID of the release to upload to :return: URL to upload for a release if found, else None
- compare_url(from_rev: str, to_rev: str) str [source]¶
Get the GitHub comparison link between two version tags. :param from_rev: The older version to compare. :param to_rev: The newer version to compare. :return: Link to view a comparison between the two versions.
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int [source]¶
Post release changelog :param version: The version number :param release_notes: The release notes for this version :return: The status of the request
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int [source]¶
Create a new release
REF: https://docs.github.com/rest/reference/repos#create-a-release
- Parameters:
tag – Tag to create release for
release_notes – The release notes for this version
prerelease – Whether or not this release should be created as a prerelease
assets – a list of artifacts to upload to the release
- Returns:
the ID of the release
- edit_release_notes(release_id: int, release_notes: str) int [source]¶
Edit a release with updated change notes https://docs.github.com/rest/reference/repos#update-a-release :param id: ID of release to update :param release_notes: The release notes for this version :return: The ID of the release that was edited
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- get_release_id_by_tag(tag: str) int | None [source]¶
Get a release by its tag name https://docs.github.com/rest/reference/repos#get-a-release-by-tag-name :param tag: Tag to get release for :return: ID of release, if found, else None
- remote_url(use_token: bool = True) str [source]¶
Get the remote url including the token for authentication if requested
- upload_dists(tag: str, dist_glob: str) int [source]¶
Upload distributions to a release :param version: Version to upload for :param path: Path to the dist directory :return: The number of distributions successfully uploaded
- upload_release_asset(release_id: int, file: str, label: str | None = None) bool [source]¶
Upload an asset to an existing release https://docs.github.com/rest/reference/repos#upload-a-release-asset :param release_id: ID of the release to upload to :param file: Path of the file to upload :param label: Optional custom label for this file :return: The status of the request
- class semantic_release.hvcs.Gitlab(remote_url: str, *, hvcs_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Gitlab HVCS interface for interacting with Gitlab repositories
- DEFAULT_DOMAIN = 'gitlab.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GITLAB_TOKEN'¶
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) str [source]¶
Post release changelog :param tag: Tag to create release for :param release_notes: The release notes for this version :param prerelease: This parameter has no effect :return: The tag of the release
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- class semantic_release.hvcs.HvcsBase(remote_url: str, *args: Any, **kwargs: Any)[source]¶
Bases:
object
Interface for subclasses interacting with a remote vcs environment
Methods generally have a base implementation are implemented here but likely just provide a not-supported message but return gracefully
This class cannot be instantated directly but must be inherited from and implement the designated abstract methods.
- abstract get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- property owner: str¶
- abstract remote_url(use_token: bool) str [source]¶
Return the remote URL for the repository, including the token for authentication if requested by setting the use_token parameter to True,
- property repo_name: str¶
- class semantic_release.hvcs.RemoteHvcsBase(remote_url: str, *args: Any, **kwargs: Any)[source]¶
Bases:
HvcsBase
Interface for subclasses interacting with a remote VCS
This abstract class is defined to provide common helper functions and a set of basic methods that all remote VCS environments usually support.
If the remote vcs implementation (via subclass) does not support a functionality then it can just call super()’s method which defaults as a non-supported log message and empty results. This is more straightforward than checking for NotImplemented around every function call in the core library code.
- DEFAULT_ENV_TOKEN_NAME = 'HVCS_TOKEN'¶
- property api_url: Url¶
- create_api_url(endpoint: str, auth: str | None = None, query: str | None = None, fragment: str | None = None) str [source]¶
- abstract create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int | str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- abstract create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int | str [source]¶
Create a release in a remote VCS, if supported
Which includes uploading any assets as part of the release
- create_repo_url(repo_path: str, query: str | None = None, fragment: str | None = None) str [source]¶
- create_server_url(path: str, auth: str | None = None, query: str | None = None, fragment: str | None = None) str [source]¶
- property hvcs_domain: Url¶
- class semantic_release.hvcs.TokenAuth(token: str)[source]¶
Bases:
AuthBase
requests Authentication for token based authorization. This allows us to attach the Authorization header with a token to a session.
Submodules¶
semantic_release.hvcs.bitbucket module¶
Helper code for interacting with a Bitbucket remote VCS
- class semantic_release.hvcs.bitbucket.Bitbucket(remote_url: str, *, hvcs_domain: str | None = None, hvcs_api_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Bitbucket HVCS interface for interacting with BitBucket repositories
This class supports the following products:
BitBucket Cloud
BitBucket Data Center Server (on-premises installations)
This interface does its best to detect which product is configured based on the provided domain. If it is the official bitbucket.org, the default domain, then it is considered as BitBucket Cloud which uses the subdomain api.bitbucket.org/2.0 for api communication.
If the provided domain is anything else, than it is assumed to be communicating with an on-premise or 3rd-party maintained BitBucket instance which matches with the BitBucket Data Center Server product. The on-prem server product uses a path prefix for handling api requests which is configured to be server.domain/rest/api/1.0 based on the documentation in April 2024.
- DEFAULT_API_PATH_CLOUD = '/2.0'¶
- DEFAULT_API_PATH_ONPREM = '/rest/api/1.0'¶
- DEFAULT_API_SUBDOMAIN_PREFIX = 'api'¶
- DEFAULT_API_URL_CLOUD = 'https://api.bitbucket.org/2.0'¶
- DEFAULT_DOMAIN = 'bitbucket.org'¶
- DEFAULT_ENV_TOKEN_NAME = 'BITBUCKET_TOKEN'¶
- compare_url(from_rev: str, to_rev: str) str [source]¶
Get the Bitbucket comparison link between two version tags. :param from_rev: The older version to compare. :param to_rev: The newer version to compare. :return: Link to view a comparison between the two versions.
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int | str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int | str [source]¶
Create a release in a remote VCS, if supported
Which includes uploading any assets as part of the release
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
semantic_release.hvcs.gitea module¶
Helper code for interacting with a Gitea remote VCS
- class semantic_release.hvcs.gitea.Gitea(remote_url: str, *, hvcs_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Gitea helper class
- DEFAULT_API_PATH = '/api/v1'¶
- DEFAULT_DOMAIN = 'gitea.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GITEA_TOKEN'¶
- asset_upload_url(release_id: str) str [source]¶
Get the correct upload url for a release https://gitea.com/api/swagger#/repository/repoCreateReleaseAttachment :param release_id: ID of the release to upload to
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int [source]¶
Post release changelog :param version: The version number :param changelog: The release notes for this version :return: The status of the request
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int [source]¶
Create a new release
Ref: https://gitea.com/api/swagger#/repository/repoCreateRelease
- Parameters:
tag – Tag to create release for
release_notes – The release notes for this version
prerelease – Whether or not this release should be specified as a
prerelease
- Returns:
Whether the request succeeded
- edit_release_notes(release_id: int, release_notes: str) int [source]¶
Edit a release with updated change notes https://gitea.com/api/swagger#/repository/repoEditRelease :param id: ID of release to update :param release_notes: The release notes for this version :return: The ID of the release that was edited
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- get_release_id_by_tag(tag: str) int | None [source]¶
Get a release by its tag name https://gitea.com/api/swagger#/repository/repoGetReleaseByTag :param tag: Tag to get release for :return: ID of found release
- remote_url(use_token: bool = True) str [source]¶
Get the remote url including the token for authentication if requested
- upload_dists(tag: str, dist_glob: str) int [source]¶
Upload distributions to a release :param tag: Tag to upload for :param path: Path to the dist directory :return: The number of distributions successfully uploaded
- upload_release_asset(release_id: int, file: str, label: str | None = None) bool [source]¶
Upload an asset to an existing release https://gitea.com/api/swagger#/repository/repoCreateReleaseAttachment :param release_id: ID of the release to upload to :param file: Path of the file to upload :param label: this parameter has no effect :return: The status of the request
semantic_release.hvcs.github module¶
Helper code for interacting with a GitHub remote VCS
- class semantic_release.hvcs.github.Github(remote_url: str, *, hvcs_domain: str | None = None, hvcs_api_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
GitHub HVCS interface for interacting with GitHub repositories
This class supports the following products:
GitHub Free, Pro, & Team
GitHub Enterprise Cloud
GitHub Enterprise Server (on-premises installations)
This interface does its best to detect which product is configured based on the provided domain. If it is the official github.com, the default domain, then it is considered as GitHub Enterprise Cloud which uses the subdomain api.github.com for api communication.
If the provided domain is anything else, than it is assumed to be communicating with an on-premise or 3rd-party maintained GitHub instance which matches with the GitHub Enterprise Server product. The on-prem server product uses a path prefix for handling api requests which is configured to be server.domain/api/v3 based on the documentation in April 2024.
- DEFAULT_API_DOMAIN = 'api.github.com'¶
- DEFAULT_API_PATH_CLOUD = '/'¶
- DEFAULT_API_PATH_ONPREM = '/api/v3'¶
- DEFAULT_API_SUBDOMAIN_PREFIX = 'api'¶
- DEFAULT_API_URL_CLOUD = 'https://api.github.com'¶
- DEFAULT_DOMAIN = 'github.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GH_TOKEN'¶
- asset_upload_url(release_id: str) str | None [source]¶
Get the correct upload url for a release https://docs.github.com/en/enterprise-server@3.5/rest/releases/releases#get-a-release :param release_id: ID of the release to upload to :return: URL to upload for a release if found, else None
- compare_url(from_rev: str, to_rev: str) str [source]¶
Get the GitHub comparison link between two version tags. :param from_rev: The older version to compare. :param to_rev: The newer version to compare. :return: Link to view a comparison between the two versions.
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int [source]¶
Post release changelog :param version: The version number :param release_notes: The release notes for this version :return: The status of the request
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int [source]¶
Create a new release
REF: https://docs.github.com/rest/reference/repos#create-a-release
- Parameters:
tag – Tag to create release for
release_notes – The release notes for this version
prerelease – Whether or not this release should be created as a prerelease
assets – a list of artifacts to upload to the release
- Returns:
the ID of the release
- edit_release_notes(release_id: int, release_notes: str) int [source]¶
Edit a release with updated change notes https://docs.github.com/rest/reference/repos#update-a-release :param id: ID of release to update :param release_notes: The release notes for this version :return: The ID of the release that was edited
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
- get_release_id_by_tag(tag: str) int | None [source]¶
Get a release by its tag name https://docs.github.com/rest/reference/repos#get-a-release-by-tag-name :param tag: Tag to get release for :return: ID of release, if found, else None
- remote_url(use_token: bool = True) str [source]¶
Get the remote url including the token for authentication if requested
- upload_dists(tag: str, dist_glob: str) int [source]¶
Upload distributions to a release :param version: Version to upload for :param path: Path to the dist directory :return: The number of distributions successfully uploaded
- upload_release_asset(release_id: int, file: str, label: str | None = None) bool [source]¶
Upload an asset to an existing release https://docs.github.com/rest/reference/repos#upload-a-release-asset :param release_id: ID of the release to upload to :param file: Path of the file to upload :param label: Optional custom label for this file :return: The status of the request
semantic_release.hvcs.gitlab module¶
Helper code for interacting with a Gitlab remote VCS
- class semantic_release.hvcs.gitlab.Gitlab(remote_url: str, *, hvcs_domain: str | None = None, token: str | None = None, allow_insecure: bool = False, **kwargs: Any)[source]¶
Bases:
RemoteHvcsBase
Gitlab HVCS interface for interacting with Gitlab repositories
- DEFAULT_DOMAIN = 'gitlab.com'¶
- DEFAULT_ENV_TOKEN_NAME = 'GITLAB_TOKEN'¶
- create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) str [source]¶
Post release changelog :param tag: Tag to create release for :param release_notes: The release notes for this version :param prerelease: This parameter has no effect :return: The tag of the release
- get_changelog_context_filters() tuple[Callable[..., Any], ...] [source]¶
Return a list of functions that can be used as filters in a Jinja2 template
ex. filters to convert text to URLs for issues and commits
semantic_release.hvcs.remote_hvcs_base module¶
Common functionality and interface for interacting with Git remote VCS
- class semantic_release.hvcs.remote_hvcs_base.RemoteHvcsBase(remote_url: str, *args: Any, **kwargs: Any)[source]¶
Bases:
HvcsBase
Interface for subclasses interacting with a remote VCS
This abstract class is defined to provide common helper functions and a set of basic methods that all remote VCS environments usually support.
If the remote vcs implementation (via subclass) does not support a functionality then it can just call super()’s method which defaults as a non-supported log message and empty results. This is more straightforward than checking for NotImplemented around every function call in the core library code.
- DEFAULT_ENV_TOKEN_NAME = 'HVCS_TOKEN'¶
- property api_url: Url¶
- create_api_url(endpoint: str, auth: str | None = None, query: str | None = None, fragment: str | None = None) str [source]¶
- abstract create_or_update_release(tag: str, release_notes: str, prerelease: bool = False) int | str [source]¶
Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported
- abstract create_release(tag: str, release_notes: str, prerelease: bool = False, assets: list[str] | None = None) int | str [source]¶
Create a release in a remote VCS, if supported
Which includes uploading any assets as part of the release
- create_repo_url(repo_path: str, query: str | None = None, fragment: str | None = None) str [source]¶
- create_server_url(path: str, auth: str | None = None, query: str | None = None, fragment: str | None = None) str [source]¶
- property hvcs_domain: Url¶
semantic_release.hvcs.token_auth module¶
semantic_release.hvcs.util module¶
- semantic_release.hvcs.util.build_requests_session(raise_for_status: bool = True, retry: bool | int | Retry = True, auth: TokenAuth | None = None) Session [source]¶
Create a requests session. :param raise_for_status: If True, a hook to invoke raise_for_status be installed :param retry: If true, it will use default Retry configuration. if an integer, it
will use default Retry configuration with given integer as total retry count. if Retry instance, it will use this instance.
- Parameters:
auth – Optional TokenAuth instance to be used to provide the Authorization header to the session
- Returns:
configured requests Session
- semantic_release.hvcs.util.suppress_http_error_for_codes(*codes: int) Callable[[Callable[[...], _R]], Callable[[...], _R | None]] [source]¶
For the codes given, return a decorator that will suppress HTTPErrors that are raised from responses that came with one of those status codes. The function will return False instead of raising the HTTPError
- semantic_release.hvcs.util.suppress_not_found(func: Callable[[...], _R]) Callable[[...], _R | None] ¶
semantic_release.version package¶
Submodules¶
semantic_release.version.algorithm module¶
- semantic_release.version.algorithm.next_version(repo: Repo, translator: VersionTranslator, commit_parser: CommitParser[ParseResult, ParserOptions], prerelease: bool = False, major_on_zero: bool = True, allow_zero_version: bool = True) Version [source]¶
Evaluate the history within repo, and based on the tags and commits in the repo history, identify the next semantic version that should be applied to a release
- semantic_release.version.algorithm.tags_and_versions(tags: Iterable[Tag], translator: VersionTranslator) list[tuple[Tag, Version]] [source]¶
Return a list of 2-tuples, where each element is a tuple (tag, version) from the tags in the Git repo and their corresponding Version according to Version.from_tag. The returned list is sorted according to semver ordering rules.
Tags which are not matched by translator are ignored.
semantic_release.version.declaration module¶
- class semantic_release.version.declaration.PatternVersionDeclaration(path: Path | str, search_text: str)[source]¶
Bases:
VersionDeclarationABC
VersionDeclarationABC implementation representing a version number in a particular file. The version number is identified by a regular expression, which should be provided in search_text.
- parse() set[semantic_release.version.version.Version] [source]¶
Return the versions matching this pattern. Because a pattern can match in multiple places, this method returns a set of matches. Generally, there should only be one element in this set (i.e. even if the version is specified in multiple places, it should be the same version in each place), but it falls on the caller to check for this condition.
- class semantic_release.version.declaration.TomlVersionDeclaration(path: Path | str, search_text: str)[source]¶
Bases:
VersionDeclarationABC
VersionDeclarationABC implementation which manages toml-format source files.
- parse() set[semantic_release.version.version.Version] [source]¶
Look for the version in the source content
- class semantic_release.version.declaration.VersionDeclarationABC(path: Path | str, search_text: str)[source]¶
Bases:
ABC
ABC for classes representing a location in which a version is declared somewhere within the source tree of the repository
- property content: str¶
The content of the source file in which the version is stored. This property is cached in the instance variable _content
- abstract parse() set[semantic_release.version.version.Version] [source]¶
Return a set of the versions which can be parsed from the file. Because a source can match in multiple places, this method returns a set of matches. Generally, there should only be one element in this set (i.e. even if the version is specified in multiple places, it should be the same version in each place), but enforcing that condition is not mandatory or expected.
- abstract replace(new_version: Version) str [source]¶
Update the versions. This method reads the underlying file, replaces each occurrence of the matched pattern, then writes the updated file. :param new_version: The new version number as a Version instance
- write(content: str) None [source]¶
Write new content back to the source path. Use alongside .replace(): >>> class MyVD(VersionDeclarationABC): … def parse(self): … … def replace(self, new_version: Version): … … def write(self, content: str): …
>>> new_version = Version.parse("1.2.3") >>> vd = MyVD("path", r"__version__ = (?P<version>\d+\d+\d+)") >>> vd.write(vd.replace(new_version))
semantic_release.version.translator module¶
- class semantic_release.version.translator.VersionTranslator(tag_format: str = 'v{version}', prerelease_token: str = 'rc')[source]¶
Bases:
object
Class to handle translation from Git tags into their corresponding Version instances.
- from_string(version_str: str) Version [source]¶
Return a Version instance from a string. Delegates directly to Version.parse, using the translator’s own stored values for tag_format and prerelease
- from_tag(tag: str) Version | None [source]¶
Return a Version instance from a Git tag, if tag_format matches the format which would have generated the tag from a version. Otherwise return None. For example, a tag of ‘v1.2.3’ should be matched if tag_format = ‘v{version}, but not if tag_format = staging–v{version}.
semantic_release.version.version module¶
- class semantic_release.version.version.Version(major: int, minor: int, patch: int, *, prerelease_token: str = 'rc', prerelease_revision: int | None = None, build_metadata: str = '', tag_format: str = 'v{version}')[source]¶
Bases:
object
- bump(level: LevelBump) Version [source]¶
Return a new Version instance according to the level specified to bump. Note this will intentionally drop the build metadata - that should be added elsewhere for the specific build producing this version.
- property is_prerelease: bool¶
- classmethod parse(version_str: str, tag_format: str = 'v{version}', prerelease_token: str = 'rc') Version [source]¶
Parse version string to a Version instance. Inspired by semver.version:VersionInfo.parse, this implementation doesn’t allow optional minor and patch versions.
- Parameters:
prerelease_token – will be ignored if the version string is a prerelease, the parsed token from version_str will be used instead.
- property tag_format: str¶
Submodules¶
semantic_release.const module¶
semantic_release.enums module¶
- class semantic_release.enums.LevelBump(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
IntEnum
IntEnum representing valid types of bumps for a version. We use an IntEnum to enable ordering of levels.
- MAJOR = 4¶
- MINOR = 3¶
- NO_RELEASE = 0¶
- PATCH = 2¶
- PRERELEASE_REVISION = 1¶
semantic_release.errors module¶
Custom Errors
- exception semantic_release.errors.AssetUploadError[source]¶
Bases:
SemanticReleaseBaseError
Raised when there is a failure uploading an asset to a remote hvcs’s release artifact storage.
- exception semantic_release.errors.CommitParseError[source]¶
Bases:
SemanticReleaseBaseError
Raised when a commit cannot be parsed by a commit parser. Custom commit parsers should also raise this Exception
- exception semantic_release.errors.IncompleteReleaseError[source]¶
Bases:
SemanticReleaseBaseError
Raised when there is a failure amongst one of the api requests when creating a release on a remote hvcs.
- exception semantic_release.errors.InvalidConfiguration[source]¶
Bases:
SemanticReleaseBaseError
Raised when configuration is deemed invalid
- exception semantic_release.errors.InvalidVersion[source]¶
Bases:
ValueError
,SemanticReleaseBaseError
Raised when Version.parse attempts to parse a string containing an invalid version.
- exception semantic_release.errors.MissingMergeBaseError[source]¶
Bases:
SemanticReleaseBaseError
Raised when the merge base cannot be found with the current history. Generally because of a shallow git clone.
- exception semantic_release.errors.NotAReleaseBranch[source]¶
Bases:
InvalidConfiguration
Raised when semantic_release is invoked on a branch which isn’t configured for releases
- exception semantic_release.errors.ParserLoadError[source]¶
Bases:
SemanticReleaseBaseError
Raised when there is a failure to find, load, or instaniate a custom parser definition.
- exception semantic_release.errors.SemanticReleaseBaseError[source]¶
Bases:
Exception
Base Exception from which all other custom Exceptions defined in semantic_release inherit
- exception semantic_release.errors.UnexpectedResponse[source]¶
Bases:
SemanticReleaseBaseError
Raised when an HTTP response cannot be parsed properly or the expected structure is not found.
semantic_release.helpers module¶
- class semantic_release.helpers.ParsedGitUrl(scheme: str, netloc: str, namespace: str, repo_name: str)[source]¶
Bases:
NamedTuple
Container for the elements parsed from a git URL
- namespace: str¶
Alias for field number 2
- netloc: str¶
Alias for field number 1
- repo_name: str¶
Alias for field number 3
- scheme: str¶
Alias for field number 0
- semantic_release.helpers.dynamic_import(import_path: str) Any [source]¶
Dynamically import an object from a conventionally formatted “module:attribute” string
- semantic_release.helpers.format_arg(value: Any) str [source]¶
Helper to format an argument an argument for logging
- semantic_release.helpers.logged_function(logger: Logger) Callable[[Callable[[...], _R]], Callable[[...], _R]] [source]¶
Decorator which adds debug logging of a function’s input arguments and return value.
The input arguments are logged before the function is called, and the return value is logged once it has completed.
- Parameters:
logger – Logger to send output to.
- semantic_release.helpers.parse_git_url(url: str) ParsedGitUrl [source]¶
Attempt to parse a string as a git url http[s]://, git://, file://, or ssh format, into a ParsedGitUrl.
- supported examples:
http://git.mycompany.com/username/myproject.git https://github.com/username/myproject.git https://gitlab.com/group/subgroup/myproject.git https://git.mycompany.com:4443/username/myproject.git git://host.xz/path/to/repo.git/ git://host.xz:9418/path/to/repo.git/ git@github.com:username/myproject.git <– assumes ssh:// ssh://git@github.com:3759/myproject.git <– non-standard, but assume user 3759 ssh://git@github.com:username/myproject.git ssh://git@bitbucket.org:7999/username/myproject.git git+ssh://git@github.com:username/myproject.git /Users/username/dev/remote/myproject.git <– Posix File paths file:///Users/username/dev/remote/myproject.git C:/Users/username/dev/remote/myproject.git <– Windows File paths file:///C:/Users/username/dev/remote/myproject.git
REFERENCE: https://stackoverflow.com/questions/31801271/what-are-the-supported-git-url-formats
Raises ValueError if the url can’t be parsed.
Python Semantic Release’s Version Bumping Algorithm¶
Below is a technical description of the algorithm which Python Semantic Release uses to calculate a new version for a project.
Assumptions¶
At runtime, we are in a Git repository with HEAD referring to a commit on some branch of the repository (i.e. not in detached HEAD state).
We know in advance whether we want to produce a prerelease or not (based on the configuration and command-line flags).
We can parse the tags of the repository into semantic versions, as we are given the format that those Git tags should follow via configuration, but cannot cherry-pick only tags that apply to commits on specific branches. We must parse all tags in order to ensure we have parsed any that might apply to commits in this branch’s history.
If we can identify a commit as a
merge-base
between our HEAD commit and one or more tags, then that merge-base should be unique.We know ahead of time what
prerelease_token
to use for prereleases - e.g.rc
.We know ahead of time whether
major
changes introduced by commits should cause the new version to remain on0.y.z
if the project is already on a0.
version - see major_on_zero.
Implementation¶
Parse all the Git tags of the repository into semantic versions, and sort in descending (most recent first) order according to semver precedence. Ignore any tags which do not correspond to valid semantic vesrions according to
tag_format
.Find the
merge-base
of HEAD and the latest tag according to the sort above. Call this commitM
. If there are no tags in the repo’s history, we setM=HEAD
.Find the latest non-prerelease version whose tag references a commit that is an ancestor of
M
. We do this via a breadth-first search through the commit lineage, starting againstM
, and for each tag checking if the tag corresponds to that commit. We break from the search when we find such a tag. If no such tag is found, see 4a). Else, suppose that tag corresponds to a commitL
- goto 4b).If no commit corresponding to the last non-prerelease version is found, the entire history of the repository is considered. We parse every commit that is an ancestor of HEAD to determine the type of change introduced - either
major
,minor
,patch
,prerelease_revision
orno_release
. We store this levels in aset
as we only require the distinct types of change that were introduced.However, if we found a commit
L
which is the commit against which the last non-prerelease was tagged, then we parse only the commits from HEAD as far back asL
, to understand what changes have been introduced since the previous non-prerelease. We store these levels - eithermajor
,minor
,patch
,prerelease_revision
, orno_release
, in a set, as we only require the distinct types of change that were introduced.We look for tags that correspond to each commit during this process, to identify the latest pre-release that was made within HEAD’s ancestry.
If there have been no changes since the last non-prerelease, or all commits since that release result in a
no_release
type according to the commit parser, then we terminate the algorithm.If we have not exited by this point, we know the following information:
The latest version, by semver precedence, within the whole repository. Call this
LV
. This might not be within the ancestry of HEAD.The latest version, prerelease or non-prerelease, within the whole repository. Call this
LVH
. This might not be within the ancestry of HEAD. This may be the same asLV
.The latest non-prerelease version within the ancestry of HEAD. Call this
LVHF
. This may be the same asLVH
.The most significant type of change introduced by the commits since the previous full release. Call this
level
Whether or not we wish to produce a prerelease from this version increment. Call this a boolean flag,
prerelease
. (Assumption)Whether or not to increment the major digit if a major change is introduced against an existing
0.
version. Call thismajor_on_zero
, a boolean flag. (Assumption)Using this information, the new version is decided according to the following criteria:
If
LV
has a major digit of0
,major_on_zero
isFalse
andlevel
ismajor
, reducelevel
tominor
.If
prerelease=True
, then
Diff
LV
withLVHF
, to understand if themajor
,minor
orpatch
digits have changed. For example, diffing1.2.1
and1.2.0
is apatch
diff, while diffing2.1.1
and1.17.2
is amajor
diff. Call thisDIFF
If
DIFF
is less semantically significant thanlevel
, for example ifDIFF=patch
andlevel=minor
, then
Increment the digit of
LVF
corresponding tolevel
, for example the minor digit iflevel=minor
, setting all less significant digits to zero.Add
prerelease_token
as a suffix result of 1., together with a prerelease revision number of1
. Return this new version and terminate the algorithm.Thus if
DIFF=patch
,level=minor
,prerelease=True
,prerelease_token="rc"
, andLVF=1.1.1
, then the version returned by the algorithm is1.2.0-rc.1
.If
DIFF
is semantically less significant than or equally significant tolevel
, then this means that the significance of change introduced bylevel
is already reflected in a prerelease version that has been created since the last full release. For example, ifLVHF=1.1.1
,LV=1.2.0-rc.1
andlevel=minor
.In this case we:
If the prerelease token of
LV
is different fromprerelease_token
, take the major, minor and patch digits ofLV
and construct a prerelease version using our givenprerelease_token
and a prerelease revision of1
. We then return this version and terminate the algorithm.For example, if
LV=1.2.0-rc.1
andprerelease_token=alpha
, we return1.2.0-alpha.1
.If the prerelease token of
LV
is the same asprerelease_token
, we increment the revision number ofLV
, return this version, andterminate the algorithm. For example, if
LV=1.2.0-rc.1
andprerelease_token=rc
, we return1.2.0-rc.2
.If
prerelease=False
, then
If
LV
is not a prerelease, then we increment the digit ofLV
corresponding tolevel
, for example the minor digit iflevel=minor
, setting all less significant digits to zero. We return the result of this and terminate the algorithm.If
LV
is a prerelease, then:
Diff
LV
withLVHF
, to understand if themajor
,minor
orpatch
digits have changed. Call thisDIFF
If
DIFF
is less semantically significant thanlevel
, then
Increment the digit of
LV
corresponding tolevel
, for example the minor digit iflevel=minor
, setting all less significant digits to zero.Remove the prerelease token and revision number from the result of i., (“Finalize” the result of i.) return the result and terminate the algorithm.
For example, if
LV=1.2.2-alpha.1
andlevel=minor
, we return1.3.0
.
If
DIFF
is semantically less significant than or equally significant tolevel
, then we finalizeLV
, return the result and terminate the algorithm.
Complexity¶
Space:
A list of parsed tags takes O(number of tags)
in space. Parsing each commit during
the breadth-first search between merge-base
and the latest tag in the ancestry
of HEAD takes at worst O(number of commits)
in space to track visited commits.
Therefore worst-case space complexity will be linear in the number of commits in the
repo, unless the number of tags significantly exceeds the number of commits
(in which case it will be linear in the number of tags).
Time:
Assuming using regular expression parsing of each tag is a constant-time operation, then the following steps contribute to the time complexity of the algorithm:
Parsing each tag -
O(number of tags)
Sorting tags by semver precedence -
O(number of tags * log(number of tags))
Finding the merge-base of HEAD and the latest release tag -
O(number of commits)
(worst case)Parsing each commit and checking each tag against each commit -
O(number of commits) + O(number of tags * number of commits)
(worst case)
Overall, assuming that the number of tags is less than or equal to the number of commits in the repository, this would lead to a worst-case time complexity that’s quadratic in the number of commits in the repo.
Getting Started¶
If you haven’t done so already, install Python Semantic Release following the instructions above.
There is no strict requirement to have it installed locally if you intend on using a CI service, however running with --noop can be useful to test your configuration.
Generating your configuration¶
Python Semantic Release ships with a command-line interface, semantic-release
. You can
inspect the default configuration in your terminal by running
semantic-release generate-config
You can also use the -f/–format option to specify what format you would like this configuration to be. The default is TOML, but JSON can also be used.
You can append the configuration to your existing pyproject.toml
file using a standard redirect,
for example:
semantic-release generate-config --pyproject >> pyproject.toml
and then editing to your project’s requirements.
Setting up version numbering¶
Create a variable set to the current version number. This could be anywhere in
your project, for example setup.py
:
from setuptools import setup
__version__ = "0.0.0"
setup(
name="my-package",
version=__version__,
# And so on...
)
Python Semantic Release can be configured using a TOML or JSON file; the default configuration file is
pyproject.toml
, if you wish to use another file you will need to use the -c/--config
option to
specify the file.
Set version_variables to a list, the only element of which should be the location of your
version variable inside any Python file, specified in standard module:attribute
syntax:
pyproject.toml
:
[tool.semantic_release]
version_variables = ["setup.py:__version__"]
See also
Configuration - tailor Python Semantic Release to your project
Setting up commit parsing¶
We rely on commit messages to detect when a version bump is needed. By default, Python Semantic Release uses the Angular style. You can find out more about this in Commit Parsing.
See also
branches - Adding configuration for releases from multiple branches.
commit_parser - use a different parser for commit messages. For example, Python Semantic Release also ships with emoji and scipy-style parsers.
remote.type - specify the type of your remote VCS.
Setting up the changelog¶
See also
Changelog - Customize the changelog generated by Python Semantic Release.
Creating VCS Releases¶
You can set up Python Semantic Release to create Releases in your remote version control system, so you can publish assets and release notes for your project.
In order to do so, you will need to place an authentication token in the appropriate environment variable so that Python Semantic Release can authenticate with the remote VCS to push tags, create releases, or upload files. You should use the appropriate steps below to set this up.
GitHub (GH_TOKEN
)¶
Use a personal access token from GitHub. See Configuring push to Github for
usage. This token should be stored in the GH_TOKEN
environment variable
To generate a token go to https://github.com/settings/tokens and click on Personal access token.
GitLab (GITLAB_TOKEN
)¶
A personal access token from GitLab. This is used for authenticating when pushing
tags, publishing releases etc. This token should be stored in the GITLAB_TOKEN
environment variable.
Gitea (GITEA_TOKEN
)¶
A personal access token from Gitea. This token should be stored in the GITEA_TOKEN
environment variable.
Bitbucket (BITBUCKET_TOKEN
)¶
Bitbucket does not support uploading releases but can still benefit from automated tags and changelogs. The user has three options to push changes to the repository:
Use SSH keys.
Use an App Secret, store the secret in the
BITBUCKET_TOKEN
environment variable and the username inBITBUCKET_USER
.Use an Access Token for the repository and store it in the
BITBUCKET_TOKEN
environment variable.
See also
Changelog - customize your project’s changelog.
Customizing VCS Release Notes - customize the VCS release notes.
upload_to_vcs_release - enable/disable uploading artefacts to VCS releases
version –vcs-release/–no-vcs-release - enable/disable VCS release creation.
upload-to-gh-release, a GitHub Action for running
semantic-release publish
Running from setup.py¶
Add the following hook to your setup.py
and you will be able to run
python setup.py <command>
as you would semantic-release <command>
:
try:
from semantic_release import setup_hook
setup_hook(sys.argv)
except ImportError:
pass
Running on CI¶
Getting a fully automated setup with releases from CI can be helpful for some projects. See Automatic Releases.
comment_start_string
¶Type:
str
This setting is passed directly to the jinja2.Environment constructor.
Default:
{#