commitizen-tools / commitizen

Create committing rules for projects :rocket: auto bump versions :arrow_up: and auto changelog generation :open_file_folder:
https://commitizen-tools.github.io/commitizen/
MIT License
2.29k stars 251 forks source link

changelog --subdirectory #365

Open yajo opened 3 years ago

yajo commented 3 years ago

Description

Allow to filter commits affecting only a subdirectory.

Possible Solution

cz changelog --subdirectory my_module --file-name my_module/CHANGELOG.md

Additional context

I'm considering proposing this for @OCA. There, repositories mostly are split into modules, where each one of them has its own readme and (maybe) changelog.

So, when autogenerating changelogs, only commits affecting the module should be taken into account. AFAICS that wouldn't be possible currently.

git format-patch allows such filtering (example recipe -- for other purposes). It would be nice if commitizen could leverage that and make this a reality.

woile commented 2 years ago

@Lee-W @Yajo

About this feature, I was thinking about adding a new setting to the configuration file:

# .cz.toml
[tool.commitizen]
workspace = true  # similar word used by rust and js

Given this folder structure (as an exmaple):

app/
└── packages/
    ├── fizzbuz/
    │   ├── pyproject.toml
    │   └── src/
    ├── foobar2/
    │   ├── .cz.toml
    │   └── src/
    └── p1/
        ├── .cz.toml
        └── src/

The user would have to navigate to each of the projects and run cz bump --changelog.

What I don't have clear is how to handle the tags? Any thoughts on this? should we attach something to the tags? otherwise they may repeat.

Lee-W commented 2 years ago

I don't get how we could use workspace = true # similar word used by rust and js. Does that mean we'll need to create a .cz.toml in the subdirectory?

woile commented 2 years ago

yes, each sub-package would have it's own .cz.toml. This means that each would have it's own independent version. This is a sample of a monorepo with poetry: https://github.com/ya-mori/python-monorepo You can see each package in libs and projects has also it's own pyproject.toml.

The alternative is to have a single .cz.toml at the top, but then commitizen should know where each project lives, something like:

members = [
 "packages/fizzbuz",
 "packages/foobar2",
 "packages/p1",
]

But this also means, they would all share the same version.

Thoughts?

Lee-W commented 2 years ago

Either way works for me. 👍 But we still have the problem of how are we going to handle the git tag? Maybe we could enforce different tag format for each project?

woile commented 2 years ago

Indeed, the tag issue only happens if you have many .cz.toml, which is the one I personally would prefer. Maybe we can add a prefix (folder-name) or something like that. I'm gonna check how lerna does it

woile commented 2 years ago

Maybe it could be part of the buildmetadata of semver.

1.0.0+fizzbuz
2.3.5+foobar2
0.1.0+p1

We just need to check if python's pep-0440 has support as well

Edit: it seems it does: https://www.python.org/dev/peps/pep-0440/#:~:text=1.0%2Bubuntu.1

In [1]: from packaging.version import Version

In [2]: v = Version("1.0.0+foobar")

In [3]: v.local
Out[3]: 'foobar'
In [1]: from semver.version import Version

In [2]: v = Version.parse("1.0.0+foobar")

In [3]: v.build
Out[3]: ['foobar']
m1racoli commented 2 years ago

What about a tag format like

submodule-v1.2.3

which represents version 1.2.3 of the module in submodule. This would be nice in a monorepo setup.

It's done like this in amundsen for example.

We're using commitizen and are currently investigating how we could consolidate multiple projects of similar nature (terraform modules, python packages, docker images,...) into a single monorepo with independent versioning and changelog.

woile commented 2 years ago

It's a possibility, but I don't think semver and pep-0440 would be able to parse a tag like that.

I get:

InvalidVersion: Invalid version: 'submodule-v1.2.3'

From my understanding the way is to add the metadata at the end with a + sign. This way, users would be able to choose to also publish those tags as a valid semver or pep-0440. This, I think is important, because even something as a helm chart requires a valid semver for example (even pep-0440 breaks when adding a rc).

yajo commented 2 years ago

What I don't have clear is how to handle the tags? Any thoughts on this? should we attach something to the tags? otherwise they may repeat.

Regarding this, for my original purpose when opening this issue, we don't use tags on those repositories.

Instead, the version is kept in a metadata file in the repo, which is specific for each module (subdirectory).

So there should be a way to configure the way we obtain the tag. Maybe in .cz.toml somehow be able to indicate a command that returns the tag for the workspace.

The alternative is to have a single .cz.toml at the top, but then commitizen should know where each project lives, something like:

members = [
 "packages/fizzbuz",
 "packages/foobar2",
 "packages/p1",
]

I prefer this alternative, to avoid maintaining a lot of .cz.toml files.

The members key could support glob patterns to ease maintenance.

But this also means, they would all share the same version.

If combined with the custom tag getter explained above, it wouldn't be a problem.


Side note: we use versions with 5 places (like 14.0.1.2.3)... is that gonna be a problem? 🤔

m1racoli commented 2 years ago

From my understanding the way is to add the metadata at the end with a + sign. This way, users would be able to choose to also publish those tags as a valid semver or pep-0440. This, I think is important, because even something as a helm chart requires a valid semver for example (even pep-0440 breaks when adding a rc).

Thanks for the input. That makes sense.

woile commented 2 years ago

Regarding this, for my original purpose when opening this issue, we don't use tags on those repositories.

I foresee some issues with the changelog generation in this situations, as we do use tags to generate the changelog itself.

Instead, the version is kept in a metadata file in the repo, which is specific for each module (subdirectory).

I prefer this alternative, to avoid maintaining a lot of .cz.toml files.

This seems like a contradiction to me. Instead of having one cz.toml per project or 1 cz.toml, you have 1 top cz.toml + 1 metadatafile. And I really don't want users to have 2 different files. A minimal .cz.toml would look like:

[tool.commitizen]
version = "2.18.0"
workspace = true

I don't think it's such a heavy burden vs a metadata file like:

2.18.0

I think eventually, support for the members could be introduced, and then cz bump could navigate the members. But having a .cz.toml per package enables also some extra benefits, like disabling changelog for specific package, using a different changelog file, different conventions per package, and all of this by just keeping the settings.

m1racoli commented 2 years ago

Going forward, the feature of generating the changelog based only on the changes of a corresponding directory is not impacted by the choice of version format, since the pattern can be freely configured already.

Thus by having a .cz.toml in each sub-folder with independent versions and setting workspace = true seems to provide the necessary functionality without breaking anything.

The question is, what happens with changes in the root folder, which potentially could affect all sub-project equally.

yajo commented 2 years ago

Instead, the version is kept in a metadata file in the repo, which is specific for each module (subdirectory).

I prefer this alternative, to avoid maintaining a lot of .cz.toml files.

This seems like a contradiction to me. Instead of having one cz.toml per project or 1 cz.toml, you have 1 top cz.toml + 1 metadatafile. And I really don't want users to have 2 different files.

Well it's not something we can change. That's the way Odoo modules work, and that's what we code there. They have a __manifest__.py file which contains the version, like in this example. And there's one per module (folder).

there should be a way to configure the way we obtain the tag

This is still relevant because otherwise all subpackages will be forced to use the same range of commits when generating the changelog.

The question is: if subpackage A is on version 1.2.3 and subpackage B is on version 0.0.1, when I go to A and execute cz bump, how does commitizen know which commits correspond to which subpackage? (They don't share the same version, so probably the repo has no git tags, or otherwise they are not relevant for the subpackage version).

One idea: what if we save in .cz.toml the commit hash of the latest bump?

The question is, what happens with changes in the root folder, which potentially could affect all sub-project equally.

IMHO the subdirectory should have a notion that it's a subdirectory and is related to a parent.

Then, just set include_parent_changes = true and those will appear. Set it to false and they won't.

I think eventually, support for the members could be introduced, and then cz bump could navigate the members. But having a .cz.toml per package enables also some extra benefits, like disabling changelog for specific package, using a different changelog file, different conventions per package, and all of this by just keeping the settings.

OK but at least I think the child .cz.toml should inherit missing keys from the parent one. The fact that a minimal file is minimal doesn't mean we'll always use minimal files. Maybe we have different commit conventions, patterns, etc., and having to repeat them every time would be boring.

jon-nfc commented 2 years ago

+1 for this feature

I currently use git log with a filter for the directory in question to create sub-directory changelogs.

Having version capabilities on a sub-directory bases would also be valuable.

robertschweizer commented 1 year ago

We have a Python monorepo with each package having its own version and changelog. The workspace option would be ideal to enable commitizen here. Right now there seems to be no way of bumping package versions only if a commit actually affected a specific package.

tabassco commented 2 months ago

I'd like to tackle this. I think tagging the version on a per-package basis using the local extension (1.0.0+lib-foo) sounds like a solid idea.

woile commented 2 months ago

Sounds great! Would you mind opening a new ticket with a formal proposal? 🙏🏻 we can discuss it there

tabassco commented 2 months ago

I'm currently playing a bit with a mutliple possible solutions. Will open a new ticket as soon as I have something concrete.