peritus / bumpversion

Version-bump your software with a single command
https://pypi.python.org/pypi/bumpversion
MIT License
1.5k stars 147 forks source link

add support for post processing with --post-hook #106

Closed mgk closed 5 years ago

mgk commented 9 years ago

Thank you so much for bumpversion. It's the link in the release process chain that I have been missing and wanting for so long

I have two use cases that I started to solve with sed and multiple bumpversion invocations before deciding there had to be a better way.

[1] replacement text with leading '#' character

This came up managing a changelog using the same pattern that bumpversion uses: insert a line for the new version when bumping. Works great with rST, but with Markdown things get interesting. I wanted to write:

[bumpversion:file:CHANGELOG.md]
search = ## [Unreleased][unreleased]
replace = ## [Unreleased][unreleased]
    ## [{new_version}] - [{now:%Y-%m-%d}]

but of course configparser treats that last line as a comment. Could add a marker and use sed or figure out another way to write the Markdown (dash underlines for level 2 instead of ## could work here). Both felt bad to me.

[2] removal of line

I ran into this copying the flow that bumpversion releases themselves use for the CHANGELOG. After bumpversion release the CHANGELOG is in good shape. After the bumpversion patch to start the unreleased dev section there is this pesky extra line. I was wondering how bumpversion dealt with it and then I saw the "grooming changelog" commits...

What I wanted was a chance to do extra stuff to the replaced text of each file so I added a --post-hook argument. With it the above use cases are solved with a config snippet like:

[bumpversion:file:CHANGELOG.md]
search = ## [Unreleased][unreleased]
replace = ## [Unreleased][unreleased]
    <<>>## [{new_version}] - [{now:%Y-%m-%d}]

and this hook:

import re

def hook(s, current_version, new_version):
    s = s.replace('<<>>', '')

    if (new_version.endswith('-dev')):
        pat = re.compile('## \[({})\]'.format(new_version))

        def dev_line(line):
            m = re.match(pat, line)
            if m:
                return m.group(1) == new_version

        lines = [line for line in s.splitlines() if not dev_line(line)]
        s = '\n'.join(lines)

    return s

I'd appreciate any feedback or suggestions, whether or not this PR is a good fit for the project.

peritus commented 9 years ago

Yes, I like the general direction of this very much (make bumpversion pluggable, create a public API in addition to the command line interface), #98 goes in that direction as well and that is the way forward.

I'd have to think about this whether we can make it more general. Exposing such an API like the one you proposed in the PR is somewhat permanent once introduced so better make this something solid.

peritus commented 9 years ago

cc/ @lgiordani

mgk commented 9 years ago

@peritus cool, yes it is as much about proposing the idea than the specific implementation.

Ideally one should be able to define per file hooks, just as search and replace can be defined per file. I looked at this, but was deterred by the multiple argument parsing passes and didn't want to break anything.

One might also want to hook different points in the cycle, perhaps having pre and post file hooks and pre and post hooks for the entire run.

lgiordani commented 9 years ago

Thanks! I like those use cases. Introducing hooks is a good idea and I agree with @peritus that it deserves a good design phase. I'll dig into it. @mgk if you have a more complete view of the whole thing feel free to post here a longer description. As Filip said we are trying to make a good step forward, and good ideas or code are always welcome.