Skip to content

Commit

Permalink
Merge pull request #29 from igordertigor/feature/allow-omitting-scope
Browse files Browse the repository at this point in the history
Feature/allow omitting scope
  • Loading branch information
igordertigor authored Sep 1, 2023
2 parents b8a8b10 + 48450fa commit a2b9505
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 20 deletions.
46 changes: 33 additions & 13 deletions docs/commit_parsing.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,58 @@ Specifically, every commit message should have the following structure:
<BLANK LINE>
<footer>
```
Where items in angle brackets (`<` and `>`) are interpreted as "variable" and will be explained below.
Where items in angle brackets (`<` and `>`) are interpreted as "variable" and
will be explained below.

Commits that do not fit this format can not be handled by semv. However, you can [configure](configuration.md) how semv behaves if it encounters an invalid commit.
Commits that do not fit this format can not be handled by semv. However, you
can [configure](configuration.md) how semv behaves if it encounters an invalid
commit.

Commit parsing is the main way how semv determines a new release version. However, semv allows using [additional checks](checks.md) to validate the version increment determined by parsing commits.
Commit parsing is the main way how semv determines a new release version.
However, semv allows using [additional checks](checks.md) to validate the
version increment determined by parsing commits.

*New*: Starting with version v2.2.0, you can now omit the scope.

## The `type` keyword

The `type` keyword is the most important part for semv. It allows semv to determine if this should be a minor or patch version increment.
In the default configuration, the type "feat" will trigger a minor release and the types "fix" and "perf" will trigger a patch release.
Other valid types are "chore" (some clean-up that needed to be done, e.g. to config or something), "test" (adding a test separately), "docs" (changes to documentation), "ci" (modifications to continuous integration setup), "refactor", and "style".
However, these other types *will not trigger a new version*.
The `type` keyword is the most important part for semv. It allows semv to
determine if this should be a minor or patch version increment. In the default
configuration, the type "feat" will trigger a minor release and the types "fix"
and "perf" will trigger a patch release. Other valid types are "chore" (some
clean-up that needed to be done, e.g. to config or something), "test" (adding a
test separately), "docs" (changes to documentation), "ci" (modifications to
continuous integration setup), "refactor", and "style". However, these other
types *will not trigger a new version*.

## The `scope` keyword

Is parsed but not used at the moment (version v1.4.5).
Is parsed but not used at the moment (version v2.2.0).

*New*: Starting with version v2.2.0, you can now omit the scope. As a result, a
commit message like "fix: General overhaul" is now valid.

## The `body` and `footer`

These are currently only parsed for lines that start with `BREAKING CHANGE: `. If any such line is found, semv will trigger a major release.
These are currently only parsed for lines that start with `BREAKING CHANGE: `.
If any such line is found, semv will trigger a major release.

## Which commits are parsed?

In order to determine the next version, all commits since the last version tag (any tag of the form vX.Y.Z) are parsed.
If the current commit is a merge commit, then all branches that lead into it are parsed and semv will analyze all commits that are not included in the last version tag. For example
In order to determine the next version, all commits since the last version tag
(any tag of the form vX.Y.Z) are parsed.
If the current commit is a merge commit, then all branches that lead into it
are parsed and semv will analyze all commits that are not included in the last
version tag. For example
```
v1.3.1
a ---> b ---> c ---> d
\ /
\ /
e -----------> f
```
In this case, commit `b` was tagged as version v1.3.1. If we call semv on commit `d`, which merges the branch `e->f->d` into the "main" branch `a->b->c->c`, the semv will analyze commits `b`, `d`, `e`, `f`.
Note that this isn't the case by default for [python semantic release](https://python-semantic-release.readthedocs.io/en/latest/commit-parsing.html).
In this case, commit `b` was tagged as version v1.3.1. If we call semv on
commit `d`, which merges the branch `e->f->d` into the "main" branch
`a->b->c->c`, the semv will analyze commits `b`, `d`, `e`, `f`.
Note that this isn't the case by default for [python semantic
release](https://python-semantic-release.readthedocs.io/en/latest/commit-parsing.html).
4 changes: 2 additions & 2 deletions src/semv/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(
skip_commit_patterns: Set[str] = set(),
):
self.type_and_scope_pattern = re.compile(
r'(?P<type>\w+)\((?P<scope>[a-zA-Z-_]+)\): .*'
r'(?P<type>\w+)\(?(?P<scope>[a-zA-Z-_]*)\)?: .*'
)
self.breaking_pattern = re.compile(
r'BREAKING CHANGE: .*', flags=re.DOTALL
Expand Down Expand Up @@ -42,7 +42,7 @@ def parse(self, commit: RawCommit) -> Optional[Commit]:
@staticmethod
def _prepare_commit(m: re.Match, sha: str, breaking: bool) -> Commit:
type = m.group('type')
scope = m.group('scope')
scope = m.group('scope') or ':global:'
return Commit(sha=sha, type=type, scope=scope, breaking=breaking)

def should_skip_by_pattern(self, title: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion tests/cram/test_fail_gracefully_if_no_commits.t
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ Now let's add a commit that has an invalid message
v0.0.0
v0.1.0
$ semv
WARNING: Invalid commit: * readme: Add some more info about our tool (glob)
WARNING: Commit * has invalid type readme (glob)
WARNING: No changes for new version
[1]
32 changes: 28 additions & 4 deletions tests/unit/test_commit_parsing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pytest
from semv.interface import RawCommit, Commit
from semv.parse import AngularCommitParser
from semv import errors
from semv.parse import AngularCommitParser, InvalidCommitAction


class TestAngularCommitParser:
Expand All @@ -8,20 +10,42 @@ def test_non_breaking(self):
assert p.parse(
RawCommit(sha='any sha', title='feat(scope): Message', body='')
) == Commit(sha='any sha', type='feat', scope='scope', breaking=False)

def test_breaking(self):
p = AngularCommitParser()
assert p.parse(
RawCommit(sha='any sha', title='feat(scope): Message', body='BREAKING CHANGE: bla bla')
RawCommit(
sha='any sha',
title='feat(scope): Message',
body='BREAKING CHANGE: bla bla',
)
) == Commit(sha='any sha', type='feat', scope='scope', breaking=True)

def test_scope_may_include_underscore(self):
p = AngularCommitParser()
assert p.parse(
RawCommit(sha='any sha', title='feat(any_scope): Message', body='')
) == Commit(sha='any sha', type='feat', scope='any_scope', breaking=False)
) == Commit(
sha='any sha', type='feat', scope='any_scope', breaking=False
)

def test_scope_may_include_dash(self):
p = AngularCommitParser()
assert p.parse(
RawCommit(sha='any sha', title='feat(any-scope): Message', body='')
) == Commit(sha='any sha', type='feat', scope='any-scope', breaking=False)
) == Commit(
sha='any sha', type='feat', scope='any-scope', breaking=False
)

def test_no_scope(self):
p = AngularCommitParser()
assert p.parse(
RawCommit(sha='any sha', title='feat: No scope', body='')
) == Commit(
sha='any sha', type='feat', scope=':global:', breaking=False
)

def test_break_scope_with_no_parens(self):
p = AngularCommitParser(InvalidCommitAction.error)
with pytest.raises(errors.InvalidCommitFormat):
p.parse(RawCommit(sha='any sha', title='feat-notscope:', body=''))

0 comments on commit a2b9505

Please sign in to comment.