1
0
Fork 0

Adding upstream version 4.6.0+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-04-21 10:42:01 +02:00
parent f3ad83a1a5
commit 167a3f8553
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
275 changed files with 30423 additions and 0 deletions

129
tests/CHANGELOG_FOR_TEST.md Normal file
View file

@ -0,0 +1,129 @@
## v1.2.0 (2019-04-19)
### feat
- custom cz plugins now support bumping version
## v1.1.1 (2019-04-18)
### refactor
- changed stdout statements
- **schema**: command logic removed from commitizen base
- **info**: command logic removed from commitizen base
- **example**: command logic removed from commitizen base
- **commit**: moved most of the commit logic to the commit command
### fix
- **bump**: commit message now fits better with semver
- conventional commit 'breaking change' in body instead of title
## v1.1.0 (2019-04-14)
### feat
- new working bump command
- create version tag
- update given files with new version
- **config**: new set key, used to set version to cfg
- support for pyproject.toml
- first semantic version bump implementation
### fix
- removed all from commit
- fix config file not working
### refactor
- added commands folder, better integration with decli
## v1.0.0 (2019-03-01)
### refactor
- removed delegator, added decli and many tests
### BREAKING CHANGE
- API is stable
## 1.0.0b2 (2019-01-18)
## v1.0.0b1 (2019-01-17)
### feat
- py3 only, tests and conventional commits 1.0
## v0.9.11 (2018-12-17)
### fix
- **config**: load config reads in order without failing if there is no commitizen section
## v0.9.10 (2018-09-22)
### fix
- parse scope (this is my punishment for not having tests)
## v0.9.9 (2018-09-22)
### fix
- parse scope empty
## v0.9.8 (2018-09-22)
### fix
- **scope**: parse correctly again
## v0.9.7 (2018-09-22)
### fix
- **scope**: parse correctly
## v0.9.6 (2018-09-19)
### refactor
- **conventionalCommit**: moved filters to questions instead of message
### fix
- **manifest**: included missing files
## v0.9.5 (2018-08-24)
### fix
- **config**: home path for python versions between 3.0 and 3.5
## v0.9.4 (2018-08-02)
### feat
- **cli**: added version
## v0.9.3 (2018-07-28)
### feat
- **committer**: conventional commit is a bit more intelligent now
## v0.9.2 (2017-11-11)
### refactor
- renamed conventional_changelog to conventional_commits, not backward compatible
## v0.9.1 (2017-11-11)
### fix
- **setup.py**: future is now required for every python version

0
tests/__init__.py Normal file
View file

View file

@ -0,0 +1,53 @@
import os
import pytest
from commitizen import defaults
from commitizen.config import BaseConfig, JsonConfig
@pytest.fixture()
def config():
_config = BaseConfig()
_config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]})
return _config
@pytest.fixture()
def config_customize():
json_string = r"""{
"commitizen": {
"name": "cz_customize",
"version": "3.0.0",
"changelog_incremental": "true",
"customize": {
"message_template": "{{prefix}}({{scope}}): {{subject}}\n\n{{body}}{% if is_breaking_change %}\nBREAKING CHANGE: {{footer}}{% endif %}",
"schema": "<type>(<scope>): <subject>\n<BLANK LINE>\n<body>\n<BLANK LINE>\n(BREAKING CHANGE: <footer>)",
"schema_pattern": "(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)(\\(\\S+\\))?!?:(\\s.*)",
"change_type_map": {
"feat": "Feat",
"fix": "Fix",
"refactor": "Refactor",
"perf": "Perf"
},
"change_type_order": ["Refactor", "Feat"],
"commit_parser": "^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\\((?P<scope>[^()\\r\\n]*)\\)|\\()?(?P<breaking>!)?:\\s(?P<message>.*)?",
"changelog_pattern": "^(BREAKING[\\-\\ ]CHANGE|feat|fix|refactor|perf)(\\(.+\\))?(!)?",
"questions": [
]
}
}
}"""
_config = JsonConfig(data=json_string, path="not_exist.json")
return _config
@pytest.fixture()
def changelog_path() -> str:
return os.path.join(os.getcwd(), "CHANGELOG.md")
@pytest.fixture()
def config_path() -> str:
return os.path.join(os.getcwd(), "pyproject.toml")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog]
[--no-verify] [--yes] [--tag-format TAG_FORMAT]
[--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}]
[--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}]
[--increment-mode {linear,exact}] [--check-consistency]
[--annotated-tag]
[--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
[--changelog-to-stdout] [--git-output-to-stderr] [--retry]
[--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
[--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
[--version-scheme {pep440,semver,semver2}]
[--version-type {pep440,semver,semver2}]
[--build-metadata BUILD_METADATA] [--get-next]
[--allow-no-commit]
[MANUAL_VERSION]
bump semantic version based on the git log
positional arguments:
MANUAL_VERSION bump to the given version (e.g: 1.5.3)
options:
-h, --help show this help message and exit
--dry-run show output to stdout, no commit, no modified files
--files-only bump version in the files from the config
--local-version bump only the local version portion
--changelog, -ch generate the changelog for the newest version
--no-verify this option bypasses the pre-commit and commit-msg
hooks
--yes accept automatically questions done
--tag-format TAG_FORMAT
the format used to tag the commit and read it, use it
in existing projects, wrap around simple quotes
--bump-message BUMP_MESSAGE
template used to create the release commit, useful
when working with CI
--prerelease, -pr {alpha,beta,rc}
choose type of prerelease
--devrelease, -d DEVRELEASE
specify non-negative integer for dev. release
--increment {MAJOR,MINOR,PATCH}
manually specify the desired increment
--increment-mode {linear,exact}
set the method by which the new version is chosen.
'linear' (default) guesses the next version based on
typical linear version progression, such that bumping
of a pre-release with lower precedence than the
current pre-release phase maintains the current phase
of higher precedence. 'exact' applies the changes that
have been specified (or determined from the commit
log) without interpretation, such that the increment
and pre-release are always honored
--check-consistency, -cc
check consistency among versions defined in commitizen
configuration and version_files
--annotated-tag, -at create annotated tag instead of lightweight one
--annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE
create annotated tag message
--gpg-sign, -s sign tag instead of lightweight one
--changelog-to-stdout
Output changelog to the stdout
--git-output-to-stderr
Redirect git output to stderr
--retry retry commit if it fails the 1st time
--major-version-zero keep major version at zero, even for breaking changes
--template, -t TEMPLATE
changelog template file name (relative to the current
working directory)
--extra, -e EXTRA a changelog extra variable (in the form 'key=value')
--file-name FILE_NAME
file name of changelog (default: 'CHANGELOG.md')
--prerelease-offset PRERELEASE_OFFSET
start pre-releases with this offset
--version-scheme {pep440,semver,semver2}
choose version scheme
--version-type {pep440,semver,semver2}
Deprecated, use --version-scheme
--build-metadata BUILD_METADATA
Add additional build-metadata to the version-number
--get-next Determine the next version and write to stdout
--allow-no-commit bump version without eligible commits

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
## Unreleased
### BREAKING CHANGE
- migrate by renaming user to users
### Feat
- **users**: email pattern corrected

View file

@ -0,0 +1,10 @@
## Unreleased
### BREAKING CHANGE
- migrate by renaming user to users
### Feat
- **users**: email pattern corrected

View file

@ -0,0 +1,11 @@
## Unreleased
### BREAKING CHANGE
- migrate by renaming user to users.
and then connect the thingy with the other thingy
### Feat
- **users**: email pattern corrected

View file

@ -0,0 +1,5 @@
## Unreleased
- drop support for py36

View file

@ -0,0 +1,6 @@
## Unreleased
### Feat
- **pipeline**: some text with breaking change

View file

@ -0,0 +1,39 @@
usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
[--unreleased-version UNRELEASED_VERSION] [--incremental]
[--start-rev START_REV] [--merge-prerelease]
[--version-scheme {pep440,semver,semver2}]
[--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
[--extra EXTRA]
[rev_range]
generate changelog (note that it will overwrite existing file)
positional arguments:
rev_range generates changelog for the given version (e.g: 1.5.3)
or version range (e.g: 1.5.3..1.7.9)
options:
-h, --help show this help message and exit
--dry-run show changelog to stdout
--file-name FILE_NAME
file name of changelog (default: 'CHANGELOG.md')
--unreleased-version UNRELEASED_VERSION
set the value for the new version (use the tag value),
instead of using unreleased
--incremental generates changelog from last created version, useful
if the changelog has been manually modified
--start-rev START_REV
start rev of the changelog. If not set, it will
generate changelog from the start
--merge-prerelease collect all changes from prereleases into next non-
prerelease. If not set, it will include prereleases in
the changelog
--version-scheme {pep440,semver,semver2}
choose version scheme
--export-template EXPORT_TEMPLATE
Export the changelog template into this file instead
of rendering it
--template, -t TEMPLATE
changelog template file name (relative to the current
working directory)
--extra, -e EXTRA a changelog extra variable (in the form 'key=value')

View file

@ -0,0 +1,8 @@
note: this should be persisted using increment
## Unreleased
### Feat
- add new output

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,7 @@
## Unreleased
### Feat
- after 0.2
- after 0.2.0

View file

@ -0,0 +1,5 @@
## 0.2.0 (2022-02-13)
### Feat
- new file

View file

@ -0,0 +1,7 @@
## 0.3.0 (2022-02-13)
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,6 @@
## 0.3.0 (2022-02-13)
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,12 @@
## 0.4.0 (2022-02-13)
### Feat
- getting ready for this
## 0.3.0 (2022-02-13)
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,12 @@
## 0.3.0 (2022-02-13)
### Feat
- another feature
- after 0.2.0
## 0.2.0 (2022-02-13)
### Feat
- new file

View file

@ -0,0 +1,17 @@
## legacy-0.4.0 (2022-02-13)
### Feat
- new file
## legacy-0.3.0 (2022-02-13)
### Feat
- new file
## old-0.2.0 (2022-02-13)
### Feat
- new file

View file

@ -0,0 +1,24 @@
## 0.5.0 (2022-02-13)
### Feat
- new shinny feature
## 0.4.1 (2022-02-13)
### Fix
- small error
## 0.4.0 (2022-02-13)
### Feat
- getting ready for this
## 0.3.0 (2022-02-13)
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,9 @@
== Unreleased
=== Feat
* new file
=== Refactor
* is in changelog

View file

@ -0,0 +1,9 @@
## Unreleased
### Feat
- new file
### Refactor
- is in changelog

View file

@ -0,0 +1,12 @@
Unreleased
==========
Feat
----
- new file
Refactor
--------
- is in changelog

View file

@ -0,0 +1,9 @@
h2. Unreleased
h3. Feat
- new file
h3. Refactor
- is in changelog

View file

@ -0,0 +1,7 @@
## Unreleased
### Feat
- after 0.2
- after 0.2.0

View file

@ -0,0 +1,16 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
# [10.0.0-rc.3](https://github.com/angular/angular/compare/10.0.0-rc.2...10.0.0-rc.3) (2020-04-22)
### Bug Fixes
* **common:** format day-periods that cross midnight ([#36611](https://github.com/angular/angular/issues/36611)) ([c6e5fc4](https://github.com/angular/angular/commit/c6e5fc4)), closes [#36566](https://github.com/angular/angular/issues/36566)

View file

@ -0,0 +1,17 @@
## v0.3.0 (2024-11-18)
### Feat
- another new file
## older-0.2.0 (2024-11-18)
### Feat
- new file
## older-0.1.0 (2024-11-18)
### Feat
- new file

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,37 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
- output glitch
## 0.2.0a0 (2023-04-16)
### Feat
- add new output
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,37 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
- output glitch
## 0.2.0b0 (2023-04-16)
### Feat
- add new output
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,37 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
- output glitch
## 0.2.0rc0 (2023-04-16)
### Feat
- add new output
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0a1 (2021-06-11)
## 0.2.0a0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0b0 (2021-06-11)
## 0.2.0a0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0rc0 (2021-06-11)
## 0.2.0a0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0b1 (2021-06-11)
## 0.2.0b0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0b1 (2021-06-11)
## 0.2.0b0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0rc0 (2021-06-11)
## 0.2.0b0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0rc1 (2021-06-11)
## 0.2.0rc0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0rc1 (2021-06-11)
## 0.2.0rc0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,32 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.0rc1 (2021-06-11)
## 0.2.0rc0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,40 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
## 0.2.0a0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,40 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
## 0.2.0b0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,40 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
## 0.2.0rc0 (2021-06-11)
### Feat
- add new output
### Fix
- output glitch
## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
### Changed
- Start using "changelog" over "change log" since it's the common usage.
### Removed
- Section about "changelog" vs "CHANGELOG".
## [0.3.0] - 2015-12-03
### Added
- RU translation from [@aishek](https://github.com/aishek).

View file

@ -0,0 +1,21 @@
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
## 0.2.0 (2022-08-14)
### Feat
- add new output
### Fix
- output glitch
note: this should be persisted using increment

View file

@ -0,0 +1,11 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- no more explosions
- output glitch

View file

@ -0,0 +1,7 @@
## 0.3.0-a0 (2022-02-13)
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,13 @@
## Unreleased
### Feat
- add more stuff
- add new output
### Fix
- mama gotta work
- output glitch
## 1.0.0 (1970-01-01)

View file

@ -0,0 +1,19 @@
== Unreleased
=== Feat
* add more stuff
=== Fix
* mama gotta work
== 0.2.0 (2022-08-14)
=== Feat
* add new output
=== Fix
* output glitch

View file

@ -0,0 +1,19 @@
## Unreleased
### Feat
- add more stuff
### Fix
- mama gotta work
## 0.2.0 (2022-08-14)
### Feat
- add new output
### Fix
- output glitch

View file

@ -0,0 +1,25 @@
Unreleased
==========
Feat
----
- add more stuff
Fix
---
- mama gotta work
0.2.0 (2022-08-14)
==================
Feat
----
- add new output
Fix
---
- output glitch

View file

@ -0,0 +1,19 @@
h2. Unreleased
h3. Feat
- add more stuff
h3. Fix
- mama gotta work
h2. 0.2.0 (2022-08-14)
h3. Feat
- add new output
h3. Fix
- output glitch

View file

@ -0,0 +1,20 @@
## 0.4.0 (2022-02-13)
### Perf
- perf improvement
### Feat
- getting ready for this
## 0.3.0 (2022-02-13)
### Fix
- fix bug
### Feat
- another feature
- after 0.2.0

View file

@ -0,0 +1,6 @@
## Unreleased
- JRA-35 #time 1w 2d 4h 30m Total work logged
- JRA-34 #comment corrected indent issue

View file

@ -0,0 +1,454 @@
from __future__ import annotations
import sys
from io import StringIO
import pytest
from pytest_mock import MockFixture
from commitizen import cli, commands, git
from commitizen.exceptions import (
InvalidCommandArgumentError,
InvalidCommitMessageError,
NoCommitsFoundError,
)
from tests.utils import create_file_and_commit, skip_below_py_3_13
COMMIT_LOG = [
"refactor: A code change that neither fixes a bug nor adds a feature",
r"refactor(cz/connventional_commit): use \S to check scope",
"refactor(git): remove unnecessary dot between git range",
"bump: version 1.16.3 → 1.16.4",
(
"Merge pull request #139 from Lee-W/fix-init-clean-config-file\n\n"
"Fix init clean config file"
),
"ci(pyproject.toml): add configuration for coverage",
"fix(commands/init): fix clean up file when initialize commitizen config\n\n#138",
"refactor(defaults): split config files into long term support and deprecated ones",
"bump: version 1.16.2 → 1.16.3",
(
"Merge pull request #136 from Lee-W/remove-redundant-readme\n\n"
"Remove redundant readme"
),
"fix: replace README.rst with docs/README.md in config files",
(
"refactor(docs): remove README.rst and use docs/README.md\n\n"
"By removing README.rst, we no longer need to maintain "
"two document with almost the same content\n"
"Github can read docs/README.md as README for the project."
),
"docs(check): pin pre-commit to v1.16.2",
"docs(check): fix pre-commit setup",
"bump: version 1.16.1 → 1.16.2",
"Merge pull request #135 from Lee-W/fix-pre-commit-hook\n\nFix pre commit hook",
"docs(check): enforce cz check only when committing",
(
'Revert "fix(pre-commit): set pre-commit check stage to commit-msg"\n\n'
"This reverts commit afc70133e4a81344928561fbf3bb20738dfc8a0b."
),
"feat!: add user stuff",
"fixup! test(commands): ignore fixup! prefix",
"fixup! test(commands): ignore squash! prefix",
]
def _build_fake_git_commits(commit_msgs: list[str]) -> list[git.GitCommit]:
return [git.GitCommit("test_rev", commit_msg) for commit_msg in commit_msgs]
def test_check_jira_fails(mocker: MockFixture):
testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="random message for J-2 #fake_command blah"),
)
with pytest.raises(InvalidCommitMessageError) as excinfo:
cli.main()
assert "commit validation: failed!" in str(excinfo.value)
def test_check_jira_command_after_issue_one_space(mocker: MockFixture, capsys):
testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="JR-23 #command some arguments etc"),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_jira_command_after_issue_two_spaces(mocker: MockFixture, capsys):
testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="JR-2 #command some arguments etc"),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_jira_text_between_issue_and_command(mocker: MockFixture, capsys):
testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="JR-234 some text #command some arguments etc"),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_jira_multiple_commands(mocker: MockFixture, capsys):
testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="JRA-23 some text #command1 args #command2 args"),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_conventional_commit_succeeds(mocker: MockFixture, capsys):
testargs = ["cz", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data="fix(scope): some commit message"),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
@pytest.mark.parametrize(
"commit_msg",
(
"feat!(lang): removed polish language",
"no conventional commit",
(
"ci: check commit message on merge\n"
"testing with more complex commit mes\n\n"
"age with error"
),
),
)
def test_check_no_conventional_commit(commit_msg, config, mocker: MockFixture, tmpdir):
with pytest.raises(InvalidCommitMessageError):
error_mock = mocker.patch("commitizen.out.error")
tempfile = tmpdir.join("temp_commit_file")
tempfile.write(commit_msg)
check_cmd = commands.Check(
config=config, arguments={"commit_msg_file": tempfile}
)
check_cmd()
error_mock.assert_called_once()
@pytest.mark.parametrize(
"commit_msg",
(
"feat(lang)!: removed polish language",
"feat(lang): added polish language",
"feat: add polish language",
"bump: 0.0.1 -> 1.0.0",
),
)
def test_check_conventional_commit(commit_msg, config, mocker: MockFixture, tmpdir):
success_mock = mocker.patch("commitizen.out.success")
tempfile = tmpdir.join("temp_commit_file")
tempfile.write(commit_msg)
check_cmd = commands.Check(config=config, arguments={"commit_msg_file": tempfile})
check_cmd()
success_mock.assert_called_once()
def test_check_command_when_commit_file_not_found(config):
with pytest.raises(FileNotFoundError):
commands.Check(config=config, arguments={"commit_msg_file": "no_such_file"})()
def test_check_a_range_of_git_commits(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
mocker.patch(
"commitizen.git.get_commits", return_value=_build_fake_git_commits(COMMIT_LOG)
)
check_cmd = commands.Check(
config=config, arguments={"rev_range": "HEAD~10..master"}
)
check_cmd()
success_mock.assert_called_once()
def test_check_a_range_of_git_commits_and_failed(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
mocker.patch(
"commitizen.git.get_commits",
return_value=_build_fake_git_commits(["This commit does not follow rule"]),
)
check_cmd = commands.Check(
config=config, arguments={"rev_range": "HEAD~10..master"}
)
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()
def test_check_command_with_invalid_argument(config):
with pytest.raises(InvalidCommandArgumentError) as excinfo:
commands.Check(
config=config,
arguments={"commit_msg_file": "some_file", "rev_range": "HEAD~10..master"},
)
assert (
"Only one of --rev-range, --message, and --commit-msg-file is permitted by check command!"
in str(excinfo.value)
)
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_check_command_with_empty_range(config, mocker: MockFixture):
# must initialize git with a commit
create_file_and_commit("feat: initial")
check_cmd = commands.Check(config=config, arguments={"rev_range": "master..master"})
with pytest.raises(NoCommitsFoundError) as excinfo:
check_cmd()
assert "No commit found with range: 'master..master'" in str(excinfo)
def test_check_a_range_of_failed_git_commits(config, mocker: MockFixture):
ill_formated_commits_msgs = [
"First commit does not follow rule",
"Second commit does not follow rule",
("Third commit does not follow rule\nIll-formatted commit with body"),
]
mocker.patch(
"commitizen.git.get_commits",
return_value=_build_fake_git_commits(ill_formated_commits_msgs),
)
check_cmd = commands.Check(
config=config, arguments={"rev_range": "HEAD~10..master"}
)
with pytest.raises(InvalidCommitMessageError) as excinfo:
check_cmd()
assert all([msg in str(excinfo.value) for msg in ill_formated_commits_msgs])
def test_check_command_with_valid_message(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
check_cmd = commands.Check(
config=config, arguments={"message": "fix(scope): some commit message"}
)
check_cmd()
success_mock.assert_called_once()
def test_check_command_with_invalid_message(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
check_cmd = commands.Check(config=config, arguments={"message": "bad commit"})
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()
def test_check_command_with_empty_message(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
check_cmd = commands.Check(config=config, arguments={"message": ""})
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()
def test_check_command_with_allow_abort_arg(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
check_cmd = commands.Check(
config=config, arguments={"message": "", "allow_abort": True}
)
check_cmd()
success_mock.assert_called_once()
def test_check_command_with_allow_abort_config(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
config.settings["allow_abort"] = True
check_cmd = commands.Check(config=config, arguments={"message": ""})
check_cmd()
success_mock.assert_called_once()
def test_check_command_override_allow_abort_config(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
config.settings["allow_abort"] = True
check_cmd = commands.Check(
config=config, arguments={"message": "", "allow_abort": False}
)
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()
def test_check_command_with_allowed_prefixes_arg(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
check_cmd = commands.Check(
config=config,
arguments={"message": "custom! test", "allowed_prefixes": ["custom!"]},
)
check_cmd()
success_mock.assert_called_once()
def test_check_command_with_allowed_prefixes_config(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
config.settings["allowed_prefixes"] = ["custom!"]
check_cmd = commands.Check(config=config, arguments={"message": "custom! test"})
check_cmd()
success_mock.assert_called_once()
def test_check_command_override_allowed_prefixes_config(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
config.settings["allow_abort"] = ["fixup!"]
check_cmd = commands.Check(
config=config,
arguments={"message": "fixup! test", "allowed_prefixes": ["custom!"]},
)
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()
def test_check_command_with_pipe_message(mocker: MockFixture, capsys):
testargs = ["cz", "check"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch("sys.stdin", StringIO("fix(scope): some commit message"))
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_command_with_pipe_message_and_failed(mocker: MockFixture):
testargs = ["cz", "check"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch("sys.stdin", StringIO("bad commit message"))
with pytest.raises(InvalidCommitMessageError) as excinfo:
cli.main()
assert "commit validation: failed!" in str(excinfo.value)
def test_check_command_with_comment_in_messege_file(mocker: MockFixture, capsys):
testargs = ["cz", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(
read_data="# <type>: (If applied, this commit will...) <subject>\n"
"# |<---- Try to Limit to a Max of 50 char ---->|\n"
"ci: add commitizen pre-commit hook\n"
"\n"
"# Explain why this change is being made\n"
"# |<---- Try To Limit Each Line to a Max Of 72 Char ---->|\n"
"This pre-commit hook will check our commits automatically."
),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
def test_check_conventional_commit_succeed_with_git_diff(mocker, capsys):
commit_msg = (
"feat: This is a test commit\n"
"# Please enter the commit message for your changes. Lines starting\n"
"# with '#' will be ignored, and an empty message aborts the commit.\n"
"#\n"
"# On branch ...\n"
"# Changes to be committed:\n"
"# modified: ...\n"
"#\n"
"# ------------------------ >8 ------------------------\n"
"# Do not modify or remove the line above.\n"
"# Everything below it will be ignored.\n"
"diff --git a/... b/...\n"
"index f1234c..1c5678 1234\n"
"--- a/...\n"
"+++ b/...\n"
"@@ -92,3 +92,4 @@ class Command(BaseCommand):\n"
'+ "this is a test"\n'
)
testargs = ["cz", "check", "--commit-msg-file", "some_file"]
mocker.patch.object(sys, "argv", testargs)
mocker.patch(
"commitizen.commands.check.open",
mocker.mock_open(read_data=commit_msg),
)
cli.main()
out, _ = capsys.readouterr()
assert "Commit validation: successful!" in out
@skip_below_py_3_13
def test_check_command_shows_description_when_use_help_option(
mocker: MockFixture, capsys, file_regression
):
testargs = ["cz", "check", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")
def test_check_command_with_message_length_limit(config, mocker: MockFixture):
success_mock = mocker.patch("commitizen.out.success")
message = "fix(scope): some commit message"
check_cmd = commands.Check(
config=config,
arguments={"message": message, "message_length_limit": len(message) + 1},
)
check_cmd()
success_mock.assert_called_once()
def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFixture):
error_mock = mocker.patch("commitizen.out.error")
message = "fix(scope): some commit message"
check_cmd = commands.Check(
config=config,
arguments={"message": message, "message_length_limit": len(message) - 1},
)
with pytest.raises(InvalidCommitMessageError):
check_cmd()
error_mock.assert_called_once()

View file

@ -0,0 +1,25 @@
usage: cz check [-h] [--commit-msg-file COMMIT_MSG_FILE |
--rev-range REV_RANGE | -m MESSAGE] [--allow-abort]
[--allowed-prefixes [ALLOWED_PREFIXES ...]]
[-l MESSAGE_LENGTH_LIMIT]
validates that a commit message matches the commitizen schema
options:
-h, --help show this help message and exit
--commit-msg-file COMMIT_MSG_FILE
ask for the name of the temporal file that contains
the commit message. Using it in a git hook script:
MSG_FILE=$1
--rev-range REV_RANGE
a range of git rev to check. e.g, master..HEAD
-m, --message MESSAGE
commit message that needs to be checked
--allow-abort allow empty commit messages, which typically abort a
commit
--allowed-prefixes [ALLOWED_PREFIXES ...]
allowed commit message prefixes. If the message starts
by one of these prefixes, the message won't be checked
against the regex
-l, --message-length-limit MESSAGE_LENGTH_LIMIT
length limit of the commit message; 0 for no limit

View file

@ -0,0 +1,527 @@
import os
import sys
from unittest.mock import ANY
import pytest
from pytest_mock import MockFixture
from commitizen import cli, cmd, commands
from commitizen.cz.exceptions import CzException
from commitizen.cz.utils import get_backup_file_path
from commitizen.exceptions import (
CommitError,
CommitMessageLengthExceededError,
CustomError,
DryRunExit,
NoAnswersError,
NoCommitBackupError,
NotAGitProjectError,
NotAllowed,
NothingToCommitError,
)
from tests.utils import skip_below_py_3_13
@pytest.fixture
def staging_is_clean(mocker: MockFixture, tmp_git_project):
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
is_staging_clean_mock.return_value = False
return tmp_git_project
@pytest.fixture
def backup_file(tmp_git_project):
with open(get_backup_file_path(), "w") as backup_file:
backup_file.write("backup commit")
@pytest.mark.usefixtures("staging_is_clean")
def test_commit(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {})()
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_backup_on_failure(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #21",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("", "error", b"", b"", 9)
error_mock = mocker.patch("commitizen.out.error")
with pytest.raises(CommitError):
commit_cmd = commands.Commit(config, {})
temp_file = commit_cmd.temp_file
commit_cmd()
prompt_mock.assert_called_once()
error_mock.assert_called_once()
assert os.path.isfile(temp_file)
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_retry_fails_no_backup(config, mocker: MockFixture):
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
with pytest.raises(NoCommitBackupError) as excinfo:
commands.Commit(config, {"retry": True})()
assert NoCommitBackupError.message in str(excinfo.value)
@pytest.mark.usefixtures("staging_is_clean", "backup_file")
def test_commit_retry_works(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commit_cmd = commands.Commit(config, {"retry": True})
temp_file = commit_cmd.temp_file
commit_cmd()
commit_mock.assert_called_with("backup commit", args="")
prompt_mock.assert_not_called()
success_mock.assert_called_once()
assert not os.path.isfile(temp_file)
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_retry_after_failure_no_backup(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #21",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["retry_after_failure"] = True
commands.Commit(config, {})()
commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="")
prompt_mock.assert_called_once()
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean", "backup_file")
def test_commit_retry_after_failure_works(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["retry_after_failure"] = True
commit_cmd = commands.Commit(config, {})
temp_file = commit_cmd.temp_file
commit_cmd()
commit_mock.assert_called_with("backup commit", args="")
prompt_mock.assert_not_called()
success_mock.assert_called_once()
assert not os.path.isfile(temp_file)
@pytest.mark.usefixtures("staging_is_clean", "backup_file")
def test_commit_retry_after_failure_with_no_retry_works(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #21",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["retry_after_failure"] = True
commit_cmd = commands.Commit(config, {"no_retry": True})
temp_file = commit_cmd.temp_file
commit_cmd()
commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="")
prompt_mock.assert_called_once()
success_mock.assert_called_once()
assert not os.path.isfile(temp_file)
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_dry_run_option(config, mocker: MockFixture):
prompt_mock = mocker = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #57",
"footer": "",
}
with pytest.raises(DryRunExit):
commit_cmd = commands.Commit(config, {"dry_run": True})
commit_cmd()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_write_message_to_file_option(
config, tmp_path, mocker: MockFixture
):
tmp_file = tmp_path / "message"
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {"write_message_to_file": tmp_file})()
success_mock.assert_called_once()
assert tmp_file.exists()
assert tmp_file.read_text() == "feat: user created"
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_invalid_write_message_to_file_option(
config, tmp_path, mocker: MockFixture
):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
with pytest.raises(NotAllowed):
commit_cmd = commands.Commit(config, {"write_message_to_file": tmp_path})
commit_cmd()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_signoff_option(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {"signoff": True})()
commit_mock.assert_called_once_with(ANY, args="-s")
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_always_signoff_enabled(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["always_signoff"] = True
commands.Commit(config, {})()
commit_mock.assert_called_once_with(ANY, args="-s")
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_gpgsign_and_always_signoff_enabled(
config, mocker: MockFixture
):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["always_signoff"] = True
commands.Commit(config, {"extra_cli_args": "-S"})()
commit_mock.assert_called_once_with(ANY, args="-S -s")
success_mock.assert_called_once()
@pytest.mark.usefixtures("tmp_git_project")
def test_commit_when_nothing_to_commit(config, mocker: MockFixture):
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
is_staging_clean_mock.return_value = True
with pytest.raises(NothingToCommitError) as excinfo:
commit_cmd = commands.Commit(config, {})
commit_cmd()
assert "No files added to staging!" in str(excinfo.value)
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_with_allow_empty(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #21",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {"extra_cli_args": "--allow-empty"})()
commit_mock.assert_called_with(
"feat: user created\n\ncloses #21", args="--allow-empty"
)
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_with_signoff_and_allow_empty(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "closes #21",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
config.settings["always_signoff"] = True
commands.Commit(config, {"extra_cli_args": "--allow-empty"})()
commit_mock.assert_called_with(
"feat: user created\n\ncloses #21", args="--allow-empty -s"
)
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_when_customized_expected_raised(config, mocker: MockFixture, capsys):
_err = ValueError()
_err.__context__ = CzException("This is the root custom err")
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.side_effect = _err
with pytest.raises(CustomError) as excinfo:
commit_cmd = commands.Commit(config, {})
commit_cmd()
# Assert only the content in the formatted text
assert "This is the root custom err" in str(excinfo.value)
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_when_non_customized_expected_raised(
config, mocker: MockFixture, capsys
):
_err = ValueError()
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.side_effect = _err
with pytest.raises(ValueError):
commit_cmd = commands.Commit(config, {})
commit_cmd()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_when_no_user_answer(config, mocker: MockFixture, capsys):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = None
with pytest.raises(NoAnswersError):
commit_cmd = commands.Commit(config, {})
commit_cmd()
def test_commit_in_non_git_project(tmpdir, config):
with tmpdir.as_cwd():
with pytest.raises(NotAGitProjectError):
commands.Commit(config, {})
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_all_option(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
add_mock = mocker.patch("commitizen.git.add")
commands.Commit(config, {"all": True})()
add_mock.assert_called()
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_extra_args(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prompt_mock.return_value = {
"prefix": "feat",
"subject": "user created",
"scope": "",
"is_breaking_change": False,
"body": "",
"footer": "",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {"extra_cli_args": "-- -extra-args1 -extra-arg2"})()
commit_mock.assert_called_once_with(ANY, args="-- -extra-args1 -extra-arg2")
success_mock.assert_called_once()
@pytest.mark.usefixtures("staging_is_clean")
def test_commit_command_with_message_length_limit(config, mocker: MockFixture):
prompt_mock = mocker.patch("questionary.prompt")
prefix = "feat"
subject = "random subject"
message_length = len(prefix) + len(": ") + len(subject)
prompt_mock.return_value = {
"prefix": prefix,
"subject": subject,
"scope": "",
"is_breaking_change": False,
"body": "random body",
"footer": "random footer",
}
commit_mock = mocker.patch("commitizen.git.commit")
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
success_mock = mocker.patch("commitizen.out.success")
commands.Commit(config, {"message_length_limit": message_length})()
success_mock.assert_called_once()
with pytest.raises(CommitMessageLengthExceededError):
commands.Commit(config, {"message_length_limit": message_length - 1})()
@pytest.mark.usefixtures("staging_is_clean")
@pytest.mark.parametrize("editor", ["vim", None])
def test_manual_edit(editor, config, mocker: MockFixture, tmp_path):
mocker.patch("commitizen.git.get_core_editor", return_value=editor)
subprocess_mock = mocker.patch("subprocess.call")
mocker.patch("shutil.which", return_value=editor)
test_message = "Initial commit message"
temp_file = tmp_path / "temp_commit_message"
temp_file.write_text(test_message)
mock_temp_file = mocker.patch("tempfile.NamedTemporaryFile")
mock_temp_file.return_value.__enter__.return_value.name = str(temp_file)
commit_cmd = commands.Commit(config, {"edit": True})
if editor is None:
with pytest.raises(RuntimeError):
commit_cmd.manual_edit(test_message)
else:
edited_message = commit_cmd.manual_edit(test_message)
subprocess_mock.assert_called_once_with(["vim", str(temp_file)])
assert edited_message == test_message.strip()
temp_file.unlink()
@skip_below_py_3_13
def test_commit_command_shows_description_when_use_help_option(
mocker: MockFixture, capsys, file_regression
):
testargs = ["cz", "commit", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,22 @@
usage: cz commit [-h] [--retry] [--no-retry] [--dry-run]
[--write-message-to-file FILE_PATH] [-s] [-a] [-e]
[-l MESSAGE_LENGTH_LIMIT] [--]
create new commit
options:
-h, --help show this help message and exit
--retry retry last commit
--no-retry skip retry if retry_after_failure is set to true
--dry-run show output to stdout, no commit, no modified files
--write-message-to-file FILE_PATH
write message to file before committing (can be
combined with --dry-run)
-s, --signoff sign off the commit
-a, --all Tell the command to automatically stage files that
have been modified and deleted, but new files you have
not told Git about are not affected.
-e, --edit edit the commit message before committing
-l, --message-length-limit MESSAGE_LENGTH_LIMIT
length limit of the commit message; 0 for no limit
-- Positional arguments separator (recommended)

View file

@ -0,0 +1,26 @@
import sys
import pytest
from pytest_mock import MockerFixture
from commitizen import cli, commands
from tests.utils import skip_below_py_3_10
def test_example(config, mocker: MockerFixture):
write_mock = mocker.patch("commitizen.out.write")
commands.Example(config)()
write_mock.assert_called_once()
@skip_below_py_3_10
def test_example_command_shows_description_when_use_help_option(
mocker: MockerFixture, capsys, file_regression
):
testargs = ["cz", "example", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,6 @@
usage: cz example [-h]
show commit example
options:
-h, --help show this help message and exit

View file

@ -0,0 +1,26 @@
import sys
import pytest
from pytest_mock import MockerFixture
from commitizen import cli, commands
from tests.utils import skip_below_py_3_10
def test_info(config, mocker: MockerFixture):
write_mock = mocker.patch("commitizen.out.write")
commands.Info(config)()
write_mock.assert_called_once()
@skip_below_py_3_10
def test_info_command_shows_description_when_use_help_option(
mocker: MockerFixture, capsys, file_regression
):
testargs = ["cz", "info", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,6 @@
usage: cz info [-h]
show information about the cz
options:
-h, --help show this help message and exit

View file

@ -0,0 +1,268 @@
from __future__ import annotations
import json
import os
import sys
from typing import Any
import pytest
import yaml
from pytest_mock import MockFixture
from commitizen import cli, commands
from commitizen.__version__ import __version__
from commitizen.exceptions import InitFailedError, NoAnswersError
from tests.utils import skip_below_py_3_10
class FakeQuestion:
def __init__(self, expected_return):
self.expected_return = expected_return
def ask(self):
return self.expected_return
def unsafe_ask(self):
return self.expected_return
pre_commit_config_filename = ".pre-commit-config.yaml"
cz_hook_config = {
"repo": "https://github.com/commitizen-tools/commitizen",
"rev": f"v{__version__}",
"hooks": [
{"id": "commitizen"},
{"id": "commitizen-branch", "stages": ["push"]},
],
}
expected_config = (
"[tool.commitizen]\n"
'name = "cz_conventional_commits"\n'
'tag_format = "$version"\n'
'version_scheme = "semver"\n'
'version = "0.0.1"\n'
"update_changelog_on_bump = true\n"
"major_version_zero = true\n"
)
EXPECTED_DICT_CONFIG = {
"commitizen": {
"name": "cz_conventional_commits",
"tag_format": "$version",
"version_scheme": "semver",
"version": "0.0.1",
"update_changelog_on_bump": True,
"major_version_zero": True,
}
}
def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config):
mocker.patch(
"questionary.select",
side_effect=[
FakeQuestion("pyproject.toml"),
FakeQuestion("cz_conventional_commits"),
FakeQuestion("commitizen"),
FakeQuestion("semver"),
],
)
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
mocker.patch("questionary.text", return_value=FakeQuestion("$version"))
# Return None to skip hook installation
mocker.patch("questionary.checkbox", return_value=FakeQuestion(None))
with tmpdir.as_cwd():
commands.Init(config)()
with open("pyproject.toml", encoding="utf-8") as toml_file:
config_data = toml_file.read()
assert config_data == expected_config
assert not os.path.isfile(pre_commit_config_filename)
def test_init_when_config_already_exists(config, capsys):
# Set config path
path = os.sep.join(["tests", "pyproject.toml"])
config.add_path(path)
commands.Init(config)()
captured = capsys.readouterr()
assert captured.out == f"Config file {path} already exists\n"
def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir):
mocker.patch(
"commitizen.commands.init.get_tag_names", return_value=["0.0.2", "0.0.1"]
)
mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="0.0.2")
mocker.patch(
"questionary.select",
side_effect=[
FakeQuestion("pyproject.toml"),
FakeQuestion("cz_conventional_commits"),
FakeQuestion("commitizen"),
FakeQuestion(""),
],
)
mocker.patch("questionary.confirm", return_value=FakeQuestion(False))
mocker.patch("questionary.text", return_value=FakeQuestion("y"))
with tmpdir.as_cwd():
with pytest.raises(NoAnswersError):
commands.Init(config)()
def test_executed_pre_commit_command(config):
init = commands.Init(config)
expected_cmd = "pre-commit install --hook-type commit-msg --hook-type pre-push"
assert init._gen_pre_commit_cmd(["commit-msg", "pre-push"]) == expected_cmd
@pytest.fixture(scope="function")
def pre_commit_installed(mocker: MockFixture):
# Assume the `pre-commit` is installed
mocker.patch(
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
return_value=True,
)
# And installation success (i.e. no exception raised)
mocker.patch(
"commitizen.commands.init.Init._exec_install_pre_commit_hook",
return_value=None,
)
@pytest.fixture(scope="function", params=["pyproject.toml", ".cz.json", ".cz.yaml"])
def default_choice(request, mocker: MockFixture):
mocker.patch(
"questionary.select",
side_effect=[
FakeQuestion(request.param),
FakeQuestion("cz_conventional_commits"),
FakeQuestion("commitizen"),
FakeQuestion("semver"),
],
)
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
mocker.patch("questionary.text", return_value=FakeQuestion("$version"))
mocker.patch(
"questionary.checkbox",
return_value=FakeQuestion(["commit-msg", "pre-push"]),
)
yield request.param
def check_cz_config(config: str):
"""
Check the content of commitizen config is as expected
Args:
config: The config path
"""
with open(config) as file:
if "json" in config:
assert json.load(file) == EXPECTED_DICT_CONFIG
elif "yaml" in config:
assert yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG
else:
config_data = file.read()
assert config_data == expected_config
def check_pre_commit_config(expected: list[dict[str, Any]]):
"""
Check the content of pre-commit config is as expected
"""
with open(pre_commit_config_filename) as pre_commit_file:
pre_commit_config_data = yaml.safe_load(pre_commit_file.read())
assert pre_commit_config_data == {"repos": expected}
@pytest.mark.usefixtures("pre_commit_installed")
class TestPreCommitCases:
def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config):
with tmpdir.as_cwd():
commands.Init(config)()
check_cz_config(default_choice)
check_pre_commit_config([cz_hook_config])
def test_empty_pre_commit_config(_, default_choice, tmpdir, config):
with tmpdir.as_cwd():
p = tmpdir.join(pre_commit_config_filename)
p.write("")
commands.Init(config)()
check_cz_config(default_choice)
check_pre_commit_config([cz_hook_config])
def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config):
existing_hook_config = {
"repo": "https://github.com/pre-commit/pre-commit-hooks",
"rev": "v1.2.3",
"hooks": [{"id", "trailing-whitespace"}],
}
with tmpdir.as_cwd():
p = tmpdir.join(pre_commit_config_filename)
p.write(yaml.safe_dump({"repos": [existing_hook_config]}))
commands.Init(config)()
check_cz_config(default_choice)
check_pre_commit_config([existing_hook_config, cz_hook_config])
def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config):
with tmpdir.as_cwd():
p = tmpdir.join(pre_commit_config_filename)
p.write(yaml.safe_dump({"repos": [cz_hook_config]}))
commands.Init(config)()
check_cz_config(default_choice)
# check that config is not duplicated
check_pre_commit_config([cz_hook_config])
class TestNoPreCommitInstalled:
def test_pre_commit_not_installed(
_, mocker: MockFixture, config, default_choice, tmpdir
):
# Assume `pre-commit` is not installed
mocker.patch(
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
return_value=False,
)
with tmpdir.as_cwd():
with pytest.raises(InitFailedError):
commands.Init(config)()
def test_pre_commit_exec_failed(
_, mocker: MockFixture, config, default_choice, tmpdir
):
# Assume `pre-commit` is installed
mocker.patch(
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
return_value=True,
)
# But pre-commit installation will fail
mocker.patch(
"commitizen.commands.init.Init._exec_install_pre_commit_hook",
side_effect=InitFailedError("Mock init failed error."),
)
with tmpdir.as_cwd():
with pytest.raises(InitFailedError):
commands.Init(config)()
@skip_below_py_3_10
def test_init_command_shows_description_when_use_help_option(
mocker: MockFixture, capsys, file_regression
):
testargs = ["cz", "init", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,6 @@
usage: cz init [-h]
init commitizen configuration
options:
-h, --help show this help message and exit

View file

@ -0,0 +1,26 @@
import sys
import pytest
from pytest_mock import MockerFixture
from commitizen import cli, commands
from tests.utils import skip_below_py_3_10
def test_list_cz(config, mocker: MockerFixture):
write_mock = mocker.patch("commitizen.out.write")
commands.ListCz(config)()
write_mock.assert_called_once()
@skip_below_py_3_10
def test_ls_command_shows_description_when_use_help_option(
mocker: MockerFixture, capsys, file_regression
):
testargs = ["cz", "ls", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,6 @@
usage: cz ls [-h]
show available commitizens
options:
-h, --help show this help message and exit

View file

@ -0,0 +1,26 @@
import sys
import pytest
from pytest_mock import MockerFixture
from commitizen import cli, commands
from tests.utils import skip_below_py_3_10
def test_schema(config, mocker: MockerFixture):
write_mock = mocker.patch("commitizen.out.write")
commands.Schema(config)()
write_mock.assert_called_once()
@skip_below_py_3_10
def test_schema_command_shows_description_when_use_help_option(
mocker: MockerFixture, capsys, file_regression
):
testargs = ["cz", "schema", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,6 @@
usage: cz schema [-h]
show commit schema
options:
-h, --help show this help message and exit

View file

@ -0,0 +1,122 @@
import platform
import sys
import pytest
from pytest_mock import MockerFixture
from commitizen import cli, commands
from commitizen.__version__ import __version__
from commitizen.config.base_config import BaseConfig
from tests.utils import skip_below_py_3_10
def test_version_for_showing_project_version(config, capsys):
# No version exist
commands.Version(
config,
{"report": False, "project": True, "commitizen": False, "verbose": False},
)()
captured = capsys.readouterr()
assert "No project information in this project." in captured.err
config.settings["version"] = "v0.0.1"
commands.Version(
config,
{"report": False, "project": True, "commitizen": False, "verbose": False},
)()
captured = capsys.readouterr()
assert "v0.0.1" in captured.out
def test_version_for_showing_commitizen_version(config, capsys):
commands.Version(
config,
{"report": False, "project": False, "commitizen": True, "verbose": False},
)()
captured = capsys.readouterr()
assert f"{__version__}" in captured.out
# default showing commitizen version
commands.Version(
config,
{"report": False, "project": False, "commitizen": False, "verbose": False},
)()
captured = capsys.readouterr()
assert f"{__version__}" in captured.out
def test_version_for_showing_both_versions(config, capsys):
commands.Version(
config,
{"report": False, "project": False, "commitizen": False, "verbose": True},
)()
captured = capsys.readouterr()
assert f"Installed Commitizen Version: {__version__}" in captured.out
assert "No project information in this project." in captured.err
config.settings["version"] = "v0.0.1"
commands.Version(
config,
{"report": False, "project": False, "commitizen": False, "verbose": True},
)()
captured = capsys.readouterr()
expected_out = (
f"Installed Commitizen Version: {__version__}\nProject Version: v0.0.1"
)
assert expected_out in captured.out
def test_version_for_showing_commitizen_system_info(config, capsys):
commands.Version(
config,
{"report": True, "project": False, "commitizen": False, "verbose": False},
)()
captured = capsys.readouterr()
assert f"Commitizen Version: {__version__}" in captured.out
assert f"Python Version: {sys.version}" in captured.out
assert f"Operating System: {platform.system()}" in captured.out
@pytest.mark.parametrize("project", (True, False))
@pytest.mark.usefixtures("tmp_git_project")
def test_version_use_version_provider(
mocker: MockerFixture,
config: BaseConfig,
capsys: pytest.CaptureFixture,
project: bool,
):
version = "0.0.0"
mock = mocker.MagicMock(name="provider")
mock.get_version.return_value = version
get_provider = mocker.patch(
"commitizen.commands.version.get_provider", return_value=mock
)
commands.Version(
config,
{
"report": False,
"project": project,
"commitizen": False,
"verbose": not project,
},
)()
captured = capsys.readouterr()
assert version in captured.out
get_provider.assert_called_once()
mock.get_version.assert_called_once()
mock.set_version.assert_not_called()
@skip_below_py_3_10
def test_version_command_shows_description_when_use_help_option(
mocker: MockerFixture, capsys, file_regression
):
testargs = ["cz", "version", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")

View file

@ -0,0 +1,12 @@
usage: cz version [-h] [-r | -p | -c | -v]
get the version of the installed commitizen or the current project (default:
installed commitizen)
options:
-h, --help show this help message and exit
-r, --report get system information for reporting bugs
-p, --project get the version of the current project
-c, --commitizen get the version of the installed commitizen
-v, --verbose get the version of both the installed commitizen and the
current project

260
tests/conftest.py Normal file
View file

@ -0,0 +1,260 @@
from __future__ import annotations
import os
import re
import tempfile
from collections.abc import Iterator
from pathlib import Path
import pytest
from pytest_mock import MockerFixture
from commitizen import cmd, defaults
from commitizen.changelog_formats import (
ChangelogFormat,
get_changelog_format,
)
from commitizen.config import BaseConfig
from commitizen.cz import registry
from commitizen.cz.base import BaseCommitizen
from tests.utils import create_file_and_commit
SIGNER = "GitHub Action"
SIGNER_MAIL = "action@github.com"
@pytest.fixture(autouse=True)
def git_sandbox(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
"""Ensure git commands are executed without the current user settings"""
# Clear any GIT_ prefixed environment variable
for var in os.environ:
if var.startswith("GIT_"):
monkeypatch.delenv(var)
# Define a dedicated temporary git config
gitconfig = tmp_path / ".git" / "config"
if not gitconfig.parent.exists():
gitconfig.parent.mkdir()
monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(gitconfig))
r = cmd.run(f"git config --file {gitconfig} user.name {SIGNER}")
assert r.return_code == 0, r.err
r = cmd.run(f"git config --file {gitconfig} user.email {SIGNER_MAIL}")
assert r.return_code == 0, r.err
r = cmd.run(f"git config --file {gitconfig} safe.directory '*'")
assert r.return_code == 0, r.err
r = cmd.run("git config --global init.defaultBranch master")
assert r.return_code == 0, r.err
@pytest.fixture
def chdir(tmp_path: Path) -> Iterator[Path]:
cwd = os.getcwd()
os.chdir(tmp_path)
yield tmp_path
os.chdir(cwd)
@pytest.fixture(scope="function")
def tmp_git_project(tmpdir):
with tmpdir.as_cwd():
cmd.run("git init")
yield tmpdir
@pytest.fixture(scope="function")
def tmp_commitizen_project(tmp_git_project):
tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
tmp_commitizen_cfg_file.write('[tool.commitizen]\nversion="0.1.0"\n')
yield tmp_git_project
@pytest.fixture(scope="function")
def tmp_commitizen_project_initial(tmp_git_project):
def _initial(
config_extra: str | None = None,
version="0.1.0",
initial_commit="feat: new user interface",
):
with tmp_git_project.as_cwd():
tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
tmp_commitizen_cfg_file.write(f'[tool.commitizen]\nversion="{version}"\n')
tmp_version_file = tmp_git_project.join("__version__.py")
tmp_version_file.write(version)
tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
tmp_version_file_string = str(tmp_version_file).replace("\\", "/")
tmp_commitizen_cfg_file.write(
f"{tmp_commitizen_cfg_file.read()}\n"
f'version_files = ["{tmp_version_file_string}"]\n'
)
if config_extra:
tmp_commitizen_cfg_file.write(config_extra, mode="a")
create_file_and_commit(initial_commit)
return tmp_git_project
yield _initial
def _get_gpg_keyid(signer_mail):
_new_key = cmd.run(f"gpg --list-secret-keys {signer_mail}")
_m = re.search(
r"[a-zA-Z0-9 \[\]-_]*\n[ ]*([0-9A-Za-z]*)\n[\na-zA-Z0-9 \[\]-_<>@]*",
_new_key.out,
)
return _m.group(1) if _m else None
@pytest.fixture(scope="function")
def tmp_commitizen_project_with_gpg(tmp_commitizen_project):
# create a temporary GPGHOME to store a temporary keyring.
# Home path must be less than 104 characters
gpg_home = tempfile.TemporaryDirectory(suffix="_cz")
if os.name != "nt":
os.environ["GNUPGHOME"] = gpg_home.name # tempdir = temp keyring
# create a key (a keyring will be generated within GPUPGHOME)
c = cmd.run(
f"gpg --batch --yes --debug-quick-random --passphrase '' --quick-gen-key '{SIGNER} {SIGNER_MAIL}'"
)
if c.return_code != 0:
raise Exception(f"gpg keygen failed with err: '{c.err}'")
key_id = _get_gpg_keyid(SIGNER_MAIL)
assert key_id
# configure git to use gpg signing
cmd.run("git config commit.gpgsign true")
cmd.run(f"git config user.signingkey {key_id}")
yield tmp_commitizen_project
@pytest.fixture()
def config():
_config = BaseConfig()
_config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]})
return _config
@pytest.fixture()
def config_path() -> str:
return os.path.join(os.getcwd(), "pyproject.toml")
class SemverCommitizen(BaseCommitizen):
"""A minimal cz rules used to test changelog and bump.
Samples:
```
minor(users): add email to user
major: removed user profile
patch(deps): updated dependency for security
```
"""
bump_pattern = r"^(patch|minor|major)"
bump_map = {
"major": "MAJOR",
"minor": "MINOR",
"patch": "PATCH",
}
bump_map_major_version_zero = {
"major": "MINOR",
"minor": "MINOR",
"patch": "PATCH",
}
changelog_pattern = r"^(patch|minor|major)"
commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)" # noqa
change_type_map = {
"major": "Breaking Changes",
"minor": "Features",
"patch": "Bugs",
}
def questions(self) -> list:
return [
{
"type": "list",
"name": "prefix",
"message": "Select the type of change you are committing",
"choices": [
{
"value": "patch",
"name": "patch: a bug fix",
"key": "p",
},
{
"value": "minor",
"name": "minor: a new feature, non-breaking",
"key": "m",
},
{
"value": "major",
"name": "major: a breaking change",
"key": "b",
},
],
},
{
"type": "input",
"name": "subject",
"message": (
"Write a short and imperative summary of the code changes: (lower case and no period)\n"
),
},
]
def message(self, answers: dict) -> str:
prefix = answers["prefix"]
subject = answers.get("subject", "default message").trim()
return f"{prefix}: {subject}"
@pytest.fixture()
def use_cz_semver(mocker):
new_cz = {**registry, "cz_semver": SemverCommitizen}
mocker.patch.dict("commitizen.cz.registry", new_cz)
class MockPlugin(BaseCommitizen):
def questions(self) -> defaults.Questions:
return []
def message(self, answers: dict) -> str:
return ""
@pytest.fixture
def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen:
mock = MockPlugin(config)
mocker.patch("commitizen.factory.commiter_factory", return_value=mock)
return mock
SUPPORTED_FORMATS = ("markdown", "textile", "asciidoc", "restructuredtext")
@pytest.fixture(params=SUPPORTED_FORMATS)
def changelog_format(
config: BaseConfig, request: pytest.FixtureRequest
) -> ChangelogFormat:
"""For tests relying on formats specifics"""
format: str = request.param
config.settings["changelog_format"] = format
if "tmp_commitizen_project" in request.fixturenames:
tmp_commitizen_project = request.getfixturevalue("tmp_commitizen_project")
pyproject = tmp_commitizen_project / "pyproject.toml"
pyproject.write(f'{pyproject.read()}\nchangelog_format = "{format}"\n')
return get_changelog_format(config)
@pytest.fixture
def any_changelog_format(config: BaseConfig) -> ChangelogFormat:
"""For test not relying on formats specifics, use the default"""
config.settings["changelog_format"] = defaults.CHANGELOG_FORMAT
return get_changelog_format(config)

View file

@ -0,0 +1,4 @@
__title__ = "requests"
__description__ = "Python HTTP for Humans."
__url__ = "http://python-requests.org"
__version__ = "2.10.3"

View file

@ -0,0 +1,27 @@
[[package]]
name = "to-update-1"
version = "1.2.9"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "to-update-2"
version = "1.2.9"

View file

@ -0,0 +1,27 @@
[[package]]
name = "to-update-1"
version = "1.2.9"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "not-to-update"
version = "1.3.3"
[[package]]
name = "to-update-2"
version = "1.2.9"

View file

@ -0,0 +1,7 @@
{
"name": "magictool",
"version": "1.2.3",
"dependencies": {
"lodash": "1.2.3"
}
}

View file

@ -0,0 +1,11 @@
[[package]]
name = "textwrap"
version = "1.2.3"
[[package]]
name = "there-i-fixed-it"
version = "1.2.3" # automatically bumped by Commitizen
[[package]]
name = "other-project"
version = "1.2.3"

View file

@ -0,0 +1,6 @@
version: "3.3"
services:
app:
image: my-repo/my-container:v1.2.3
command: my-command

View file

@ -0,0 +1,3 @@
[tool.poetry]
name = "commitizen"
version = "1.2.3"

View file

@ -0,0 +1,4 @@
__title__ = "requests"
__description__ = "Python HTTP for Humans."
__url__ = "http://python-requests.org"
__version__ = "1.2.3"

View file

@ -0,0 +1,15 @@
from __future__ import annotations
import os
from collections.abc import Iterator
from pathlib import Path
import pytest
@pytest.fixture
def chdir(tmp_path: Path) -> Iterator[Path]:
cwd = Path()
os.chdir(tmp_path)
yield tmp_path
os.chdir(cwd)

View file

@ -0,0 +1,20 @@
from __future__ import annotations
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.exceptions import VersionProviderUnknown
from commitizen.providers import get_provider
from commitizen.providers.commitizen_provider import CommitizenProvider
def test_default_version_provider_is_commitizen_config(config: BaseConfig):
provider = get_provider(config)
assert isinstance(provider, CommitizenProvider)
def test_raise_for_unknown_provider(config: BaseConfig):
config.settings["version_provider"] = "unknown"
with pytest.raises(VersionProviderUnknown):
get_provider(config)

View file

@ -0,0 +1,60 @@
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.cargo_provider import CargoProvider
CARGO_TOML = """\
[package]
name = "whatever"
version = "0.1.0"
"""
CARGO_EXPECTED = """\
[package]
name = "whatever"
version = "42.1"
"""
CARGO_WORKSPACE_TOML = """\
[workspace.package]
name = "whatever"
version = "0.1.0"
"""
CARGO_WORKSPACE_EXPECTED = """\
[workspace.package]
name = "whatever"
version = "42.1"
"""
@pytest.mark.parametrize(
"content, expected",
(
(CARGO_TOML, CARGO_EXPECTED),
(CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_EXPECTED),
),
)
def test_cargo_provider(
config: BaseConfig,
chdir: Path,
content: str,
expected: str,
):
filename = CargoProvider.filename
file = chdir / filename
file.write_text(dedent(content))
config.settings["version_provider"] = "cargo"
provider = get_provider(config)
assert isinstance(provider, CargoProvider)
assert provider.get_version() == "0.1.0"
provider.set_version("42.1")
assert file.read_text() == dedent(expected)

View file

@ -0,0 +1,20 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from commitizen.config.base_config import BaseConfig
from commitizen.providers.commitizen_provider import CommitizenProvider
if TYPE_CHECKING:
from pytest_mock import MockerFixture
def test_commitizen_provider(config: BaseConfig, mocker: MockerFixture):
config.settings["version"] = "42"
mock = mocker.patch.object(config, "set_key")
provider = CommitizenProvider(config)
assert provider.get_version() == "42"
provider.set_version("43.1")
mock.assert_called_once_with("version", "43.1")

View file

@ -0,0 +1,47 @@
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.composer_provider import ComposerProvider
COMPOSER_JSON = """\
{
"name": "whatever",
"version": "0.1.0"
}
"""
COMPOSER_EXPECTED = """\
{
"name": "whatever",
"version": "42.1"
}
"""
@pytest.mark.parametrize(
"content, expected",
((COMPOSER_JSON, COMPOSER_EXPECTED),),
)
def test_composer_provider(
config: BaseConfig,
chdir: Path,
content: str,
expected: str,
):
filename = ComposerProvider.filename
file = chdir / filename
file.write_text(dedent(content))
config.settings["version_provider"] = "composer"
provider = get_provider(config)
assert isinstance(provider, ComposerProvider)
assert provider.get_version() == "0.1.0"
provider.set_version("42.1")
assert file.read_text() == dedent(expected)

View file

@ -0,0 +1,98 @@
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.npm_provider import NpmProvider
NPM_PACKAGE_JSON = """\
{
"name": "whatever",
"version": "0.1.0"
}
"""
NPM_PACKAGE_EXPECTED = """\
{
"name": "whatever",
"version": "42.1"
}
"""
NPM_LOCKFILE_JSON = """\
{
"name": "whatever",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "whatever",
"version": "0.1.0"
},
"someotherpackage": {
"version": "0.1.0"
}
}
}
"""
NPM_LOCKFILE_EXPECTED = """\
{
"name": "whatever",
"version": "42.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "whatever",
"version": "42.1"
},
"someotherpackage": {
"version": "0.1.0"
}
}
}
"""
@pytest.mark.parametrize(
"pkg_shrinkwrap_content, pkg_shrinkwrap_expected",
((NPM_LOCKFILE_JSON, NPM_LOCKFILE_EXPECTED), (None, None)),
)
@pytest.mark.parametrize(
"pkg_lock_content, pkg_lock_expected",
((NPM_LOCKFILE_JSON, NPM_LOCKFILE_EXPECTED), (None, None)),
)
def test_npm_provider(
config: BaseConfig,
chdir: Path,
pkg_lock_content: str,
pkg_lock_expected: str,
pkg_shrinkwrap_content: str,
pkg_shrinkwrap_expected: str,
):
pkg = chdir / NpmProvider.package_filename
pkg.write_text(dedent(NPM_PACKAGE_JSON))
if pkg_lock_content:
pkg_lock = chdir / NpmProvider.lock_filename
pkg_lock.write_text(dedent(pkg_lock_content))
if pkg_shrinkwrap_content:
pkg_shrinkwrap = chdir / NpmProvider.shrinkwrap_filename
pkg_shrinkwrap.write_text(dedent(pkg_shrinkwrap_content))
config.settings["version_provider"] = "npm"
provider = get_provider(config)
assert isinstance(provider, NpmProvider)
assert provider.get_version() == "0.1.0"
provider.set_version("42.1")
assert pkg.read_text() == dedent(NPM_PACKAGE_EXPECTED)
if pkg_lock_content:
assert pkg_lock.read_text() == dedent(pkg_lock_expected)
if pkg_shrinkwrap_content:
assert pkg_shrinkwrap.read_text() == dedent(pkg_shrinkwrap_expected)

View file

@ -0,0 +1,43 @@
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.pep621_provider import Pep621Provider
PEP621_TOML = """\
[project]
version = "0.1.0"
"""
PEP621_EXPECTED = """\
[project]
version = "42.1"
"""
@pytest.mark.parametrize(
"content, expected",
((PEP621_TOML, PEP621_EXPECTED),),
)
def test_cargo_provider(
config: BaseConfig,
chdir: Path,
content: str,
expected: str,
):
filename = Pep621Provider.filename
file = chdir / filename
file.write_text(dedent(content))
config.settings["version_provider"] = "pep621"
provider = get_provider(config)
assert isinstance(provider, Pep621Provider)
assert provider.get_version() == "0.1.0"
provider.set_version("42.1")
assert file.read_text() == dedent(expected)

View file

@ -0,0 +1,43 @@
from __future__ import annotations
from pathlib import Path
from textwrap import dedent
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.poetry_provider import PoetryProvider
POETRY_TOML = """\
[tool.poetry]
version = "0.1.0"
"""
POETRY_EXPECTED = """\
[tool.poetry]
version = "42.1"
"""
@pytest.mark.parametrize(
"content, expected",
((POETRY_TOML, POETRY_EXPECTED),),
)
def test_cargo_provider(
config: BaseConfig,
chdir: Path,
content: str,
expected: str,
):
filename = PoetryProvider.filename
file = chdir / filename
file.write_text(dedent(content))
config.settings["version_provider"] = "poetry"
provider = get_provider(config)
assert isinstance(provider, PoetryProvider)
assert provider.get_version() == "0.1.0"
provider.set_version("42.1")
assert file.read_text() == dedent(expected)

View file

@ -0,0 +1,138 @@
from __future__ import annotations
import pytest
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.scm_provider import ScmProvider
from tests.utils import (
create_branch,
create_file_and_commit,
create_tag,
merge_branch,
switch_branch,
)
@pytest.mark.parametrize(
"tag_format,tag,expected_version",
(
# If tag_format is $version (the default), version_scheme.parser is used.
# Its DEFAULT_VERSION_PARSER allows a v prefix, but matches PEP440 otherwise.
("$version", "no-match-because-version-scheme-is-strict", "0.0.0"),
("$version", "0.1.0", "0.1.0"),
("$version", "v0.1.0", "0.1.0"),
("$version", "v-0.1.0", "0.0.0"),
# If tag_format is not None or $version, TAG_FORMAT_REGEXS are used, which are
# much more lenient but require a v prefix.
("v$version", "v0.1.0", "0.1.0"),
("v$version", "no-match-because-no-v-prefix", "0.0.0"),
# no match because not a valid version
("v$version", "v-match-TAG_FORMAT_REGEXS", "0.0.0"),
("version-$version", "version-0.1.0", "0.1.0"),
("version-$version", "version-0.1", "0.1"),
("version-$version", "version-0.1.0rc1", "0.1.0rc1"),
("v$minor.$major.$patch", "v1.0.0", "0.1.0"),
("version-$major.$minor.$patch", "version-0.1.0", "0.1.0"),
("v$major.$minor$prerelease$devrelease", "v1.0rc1", "1.0rc1"),
("v$major.$minor.$patch$prerelease$devrelease", "v0.1.0", "0.1.0"),
("v$major.$minor.$patch$prerelease$devrelease", "v0.1.0rc1", "0.1.0rc1"),
("v$major.$minor.$patch$prerelease$devrelease", "v1.0.0.dev0", "1.0.0.dev0"),
),
)
@pytest.mark.usefixtures("tmp_git_project")
def test_scm_provider(
config: BaseConfig, tag_format: str, tag: str, expected_version: str
):
create_file_and_commit("test: fake commit")
create_tag(tag)
create_file_and_commit("test: fake commit")
create_tag("should-not-match")
config.settings["version_provider"] = "scm"
config.settings["tag_format"] = tag_format
provider = get_provider(config)
assert isinstance(provider, ScmProvider)
actual_version = provider.get_version()
assert actual_version == expected_version
# Should not fail on set_version()
provider.set_version("43.1")
@pytest.mark.usefixtures("tmp_git_project")
def test_scm_provider_default_without_commits_and_tags(config: BaseConfig):
config.settings["version_provider"] = "scm"
provider = get_provider(config)
assert isinstance(provider, ScmProvider)
assert provider.get_version() == "0.0.0"
@pytest.mark.usefixtures("tmp_git_project")
def test_scm_provider_default_with_commits_and_tags(config: BaseConfig):
config.settings["version_provider"] = "scm"
provider = get_provider(config)
assert isinstance(provider, ScmProvider)
assert provider.get_version() == "0.0.0"
create_file_and_commit("Initial state")
create_tag("1.0.0")
# create develop
create_branch("develop")
switch_branch("develop")
# add a feature to develop
create_file_and_commit("develop: add beta feature1")
assert provider.get_version() == "1.0.0"
create_tag("1.1.0b0")
# create staging
create_branch("staging")
switch_branch("staging")
create_file_and_commit("staging: Starting release candidate")
assert provider.get_version() == "1.1.0b0"
create_tag("1.1.0rc0")
# add another feature to develop
switch_branch("develop")
create_file_and_commit("develop: add beta feature2")
assert provider.get_version() == "1.1.0b0"
create_tag("1.2.0b0")
# add a hotfix to master
switch_branch("master")
create_file_and_commit("master: add hotfix")
assert provider.get_version() == "1.0.0"
create_tag("1.0.1")
# merge the hotfix to staging
switch_branch("staging")
merge_branch("master")
assert provider.get_version() == "1.1.0rc0"
@pytest.mark.usefixtures("tmp_git_project")
def test_scm_provider_detect_legacy_tags(config: BaseConfig):
config.settings["version_provider"] = "scm"
config.settings["tag_format"] = "v${version}"
config.settings["legacy_tag_formats"] = [
"legacy-${version}",
"old-${version}",
]
provider = get_provider(config)
create_file_and_commit("test: fake commit")
create_tag("old-0.4.1")
assert provider.get_version() == "0.4.1"
create_file_and_commit("test: fake commit")
create_tag("legacy-0.4.2")
assert provider.get_version() == "0.4.2"
create_file_and_commit("test: fake commit")
create_tag("v0.5.0")
assert provider.get_version() == "0.5.0"

View file

@ -0,0 +1,97 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from commitizen.config.base_config import BaseConfig
from commitizen.providers import get_provider
from commitizen.providers.uv_provider import UvProvider
if TYPE_CHECKING:
from pytest_regressions.file_regression import FileRegressionFixture
PYPROJECT_TOML = """
[project]
name = "test-uv"
version = "4.2.1"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = ["commitizen==4.2.1"]
"""
UV_LOCK_SIMPLIFIED = """
version = 1
revision = 1
requires-python = ">=3.13"
[[package]]
name = "commitizen"
version = "4.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "argcomplete" },
{ name = "charset-normalizer" },
{ name = "colorama" },
{ name = "decli" },
{ name = "jinja2" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "questionary" },
{ name = "termcolor" },
{ name = "tomlkit" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/a3/77ffc9aee014cbf46c84c9f156a1ddef2d4c7cfb87d567decf2541464245/commitizen-4.2.1.tar.gz", hash = "sha256:5255416f6d6071068159f0b97605777f3e25d00927ff157b7a8d01efeda7b952", size = 50645 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/57/ce/2f5d8ebe8376991b5f805e9f33d20c7f4c9ca6155bdbda761117dc41dff1/commitizen-4.2.1-py3-none-any.whl", hash = "sha256:a347889e0fe408c3b920a34130d8f35616be3ea8ac6b7b20c5b9aac19762661b", size = 72646 },
]
[[package]]
name = "decli"
version = "0.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 },
]
[[package]]
name = "test-uv"
version = "4.2.1"
source = { virtual = "." }
dependencies = [
{ name = "commitizen" },
]
"""
def test_uv_provider(
config: BaseConfig, tmpdir, file_regression: FileRegressionFixture
):
with tmpdir.as_cwd():
pyproject_toml_file = tmpdir / UvProvider.filename
pyproject_toml_file.write_text(PYPROJECT_TOML, encoding="utf-8")
uv_lock_file = tmpdir / UvProvider.lock_filename
uv_lock_file.write_text(UV_LOCK_SIMPLIFIED, encoding="utf-8")
config.settings["version_provider"] = "uv"
provider = get_provider(config)
assert isinstance(provider, UvProvider)
assert provider.get_version() == "4.2.1"
provider.set_version("100.100.100")
assert provider.get_version() == "100.100.100"
updated_pyproject_toml_content = pyproject_toml_file.read_text(encoding="utf-8")
updated_uv_lock_content = uv_lock_file.read_text(encoding="utf-8")
for content in (updated_pyproject_toml_content, updated_uv_lock_content):
# updated project version
assert "100.100.100" in content
# commitizen version which was the same as project version and should not be affected
assert "4.2.1" in content
file_regression.check(updated_pyproject_toml_content, extension=".toml")
file_regression.check(updated_uv_lock_content, extension=".lock")

View file

@ -0,0 +1,42 @@
version = 1
revision = 1
requires-python = ">=3.13"
[[package]]
name = "commitizen"
version = "4.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "argcomplete" },
{ name = "charset-normalizer" },
{ name = "colorama" },
{ name = "decli" },
{ name = "jinja2" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "questionary" },
{ name = "termcolor" },
{ name = "tomlkit" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/a3/77ffc9aee014cbf46c84c9f156a1ddef2d4c7cfb87d567decf2541464245/commitizen-4.2.1.tar.gz", hash = "sha256:5255416f6d6071068159f0b97605777f3e25d00927ff157b7a8d01efeda7b952", size = 50645 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/57/ce/2f5d8ebe8376991b5f805e9f33d20c7f4c9ca6155bdbda761117dc41dff1/commitizen-4.2.1-py3-none-any.whl", hash = "sha256:a347889e0fe408c3b920a34130d8f35616be3ea8ac6b7b20c5b9aac19762661b", size = 72646 },
]
[[package]]
name = "decli"
version = "0.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 },
]
[[package]]
name = "test-uv"
version = "100.100.100"
source = { virtual = "." }
dependencies = [
{ name = "commitizen" },
]

View file

@ -0,0 +1,8 @@
[project]
name = "test-uv"
version = "100.100.100"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = ["commitizen==4.2.1"]

View file

@ -0,0 +1,160 @@
import sys
from pathlib import Path
from textwrap import dedent
import pytest
from pytest_mock import MockFixture
from commitizen import bump, cli, cmd, exceptions
conversion = [
(
("1.2.3", "1.3.0", "bump: $current_version -> $new_version [skip ci]"),
"bump: 1.2.3 -> 1.3.0 [skip ci]",
),
(("1.2.3", "1.3.0", None), "bump: version 1.2.3 → 1.3.0"),
(("1.2.3", "1.3.0", "release $new_version"), "release 1.3.0"),
]
@pytest.mark.parametrize("test_input,expected", conversion)
def test_create_tag(test_input, expected):
current_version, new_version, message_template = test_input
new_tag = bump.create_commit_message(current_version, new_version, message_template)
assert new_tag == expected
@pytest.mark.parametrize(
"retry",
(
pytest.param(
True,
marks=pytest.mark.skipif(
sys.version_info >= (3, 13),
reason="mirrors-prettier is not supported with Python 3.13 or higher",
),
),
False,
),
)
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_bump_pre_commit_changelog(mocker: MockFixture, freezer, retry):
freezer.move_to("2022-04-01")
testargs = ["cz", "bump", "--changelog", "--yes"]
if retry:
testargs.append("--retry")
else:
pytest.xfail("it will fail because pre-commit will reformat CHANGELOG.md")
mocker.patch.object(sys, "argv", testargs)
# Configure prettier as a pre-commit hook
Path(".pre-commit-config.yaml").write_text(
dedent(
"""\
repos:
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
hooks:
- id: prettier
stages: [commit]
"""
)
)
# Prettier inherits editorconfig
Path(".editorconfig").write_text(
dedent(
"""\
[*]
indent_size = 4
"""
)
)
cmd.run("git add -A")
cmd.run('git commit -m "fix: _test"')
cmd.run("pre-commit install")
cli.main()
# Pre-commit fixed last line adding extra indent and "\" char
assert Path("CHANGELOG.md").read_text() == dedent(
"""\
## 0.1.1 (2022-04-01)
### Fix
- \\_test
"""
)
@pytest.mark.parametrize("retry", (True, False))
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_bump_pre_commit_changelog_fails_always(mocker: MockFixture, freezer, retry):
freezer.move_to("2022-04-01")
testargs = ["cz", "bump", "--changelog", "--yes"]
if retry:
testargs.append("--retry")
mocker.patch.object(sys, "argv", testargs)
Path(".pre-commit-config.yaml").write_text(
dedent(
"""\
repos:
- repo: local
hooks:
- id: forbid-changelog
name: changelogs are forbidden
entry: changelogs are forbidden
language: fail
files: CHANGELOG.md
"""
)
)
cmd.run("git add -A")
cmd.run('git commit -m "feat: forbid changelogs"')
cmd.run("pre-commit install")
with pytest.raises(exceptions.BumpCommitFailedError):
cli.main()
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_bump_with_build_metadata(mocker: MockFixture, freezer):
def _add_entry(test_str: str, args: list):
Path(test_str).write_text("")
cmd.run("git add -A")
cmd.run(f'git commit -m "fix: test-{test_str}"')
cz_args = ["cz", "bump", "--changelog", "--yes"] + args
mocker.patch.object(sys, "argv", cz_args)
cli.main()
freezer.move_to("2024-01-01")
_add_entry("a", ["--build-metadata", "a.b.c"])
_add_entry("b", [])
_add_entry("c", ["--build-metadata", "alongmetadatastring"])
_add_entry("d", [])
# Pre-commit fixed last line adding extra indent and "\" char
assert Path("CHANGELOG.md").read_text() == dedent(
"""\
## 0.1.4 (2024-01-01)
### Fix
- test-d
## 0.1.3+alongmetadatastring (2024-01-01)
### Fix
- test-c
## 0.1.2 (2024-01-01)
### Fix
- test-b
## 0.1.1+a.b.c (2024-01-01)
### Fix
- test-a
"""
)

View file

@ -0,0 +1,124 @@
"""
CC: Conventional commits
SVE: Semantic version at the end
"""
import pytest
from commitizen import bump
from commitizen.cz.conventional_commits import ConventionalCommitsCz
from commitizen.git import GitCommit
NONE_INCREMENT_CC = [
"docs(README): motivation",
"ci: added travis",
"performance. Remove or disable the reimplemented linters",
"refactor that how this line starts",
]
PATCH_INCREMENTS_CC = [
"fix(setup.py): future is now required for every python version",
"docs(README): motivation",
]
MINOR_INCREMENTS_CC = [
"feat(cli): added version",
"docs(README): motivation",
"fix(setup.py): future is now required for every python version",
"perf: app is much faster",
"refactor: app is much faster",
]
MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [
"feat(cli): added version",
"docs(README): motivation",
"BREAKING CHANGE: `extends` key in config file is now used for extending other config files", # noqa
"fix(setup.py): future is now required for every python version",
]
MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC = [
"feat(cli): added version",
"docs(README): motivation",
"BREAKING-CHANGE: `extends` key in config file is now used for extending other config files", # noqa
"fix(setup.py): future is now required for every python version",
]
MAJOR_INCREMENTS_EXCLAMATION_CC = [
"feat(cli)!: added version",
"docs(README): motivation",
"fix(setup.py): future is now required for every python version",
]
MAJOR_INCREMENTS_EXCLAMATION_CC_SAMPLE_2 = [
"feat(pipeline)!: some text with breaking change"
]
MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_CC = [
"chore!: drop support for Python 3.9",
"docs(README): motivation",
"fix(setup.py): future is now required for every python version",
]
MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_WITH_SCOPE_CC = [
"chore(deps)!: drop support for Python 3.9",
"docs(README): motivation",
"fix(setup.py): future is now required for every python version",
]
PATCH_INCREMENTS_SVE = ["readme motivation PATCH", "fix setup.py PATCH"]
MINOR_INCREMENTS_SVE = [
"readme motivation PATCH",
"fix setup.py PATCH",
"added version to cli MINOR",
]
MAJOR_INCREMENTS_SVE = [
"readme motivation PATCH",
"fix setup.py PATCH",
"added version to cli MINOR",
"extends key is used for other config files MAJOR",
]
semantic_version_pattern = r"(MAJOR|MINOR|PATCH)"
semantic_version_map = {"MAJOR": "MAJOR", "MINOR": "MINOR", "PATCH": "PATCH"}
@pytest.mark.parametrize(
"messages, expected_type",
(
(PATCH_INCREMENTS_CC, "PATCH"),
(MINOR_INCREMENTS_CC, "MINOR"),
(MAJOR_INCREMENTS_BREAKING_CHANGE_CC, "MAJOR"),
(MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC, "MAJOR"),
(MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_CC, "MAJOR"),
(MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_WITH_SCOPE_CC, "MAJOR"),
(MAJOR_INCREMENTS_EXCLAMATION_CC, "MAJOR"),
(MAJOR_INCREMENTS_EXCLAMATION_CC_SAMPLE_2, "MAJOR"),
(NONE_INCREMENT_CC, None),
),
)
def test_find_increment(messages, expected_type):
commits = [GitCommit(rev="test", title=message) for message in messages]
increment_type = bump.find_increment(
commits,
regex=ConventionalCommitsCz.bump_pattern,
increments_map=ConventionalCommitsCz.bump_map,
)
assert increment_type == expected_type
@pytest.mark.parametrize(
"messages, expected_type",
(
(PATCH_INCREMENTS_SVE, "PATCH"),
(MINOR_INCREMENTS_SVE, "MINOR"),
(MAJOR_INCREMENTS_SVE, "MAJOR"),
),
)
def test_find_increment_sve(messages, expected_type):
commits = [GitCommit(rev="test", title=message) for message in messages]
increment_type = bump.find_increment(
commits, regex=semantic_version_pattern, increments_map=semantic_version_map
)
assert increment_type == expected_type

Some files were not shown because too many files have changed in this diff Show more