{"id":2720,"date":"2026-06-21T05:29:48","date_gmt":"2026-06-21T03:29:48","guid":{"rendered":"https:\/\/blogs.gentoo.org\/mgorny\/?p=2720"},"modified":"2026-06-21T05:29:48","modified_gmt":"2026-06-21T03:29:48","slug":"pkgbump-from-a-dumb-tool-to-an-irreplaceable-helper","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/mgorny\/2026\/06\/21\/pkgbump-from-a-dumb-tool-to-an-irreplaceable-helper\/","title":{"rendered":"pkgbump: from a dumb tool to an irreplaceable helper"},"content":{"rendered":"<p>Bumping packages is one of the most common tasks of a Gentoo developer. It shouldn&#8217;t then be surprising that it is the one most asking for some kind of automation, and that the <kbd>pkgbump<\/kbd> script would be one of the first scripts to become a part of the <a rel=\"external\" href=\"https:\/\/gitweb.gentoo.org\/proj\/mgorny-dev-scripts.git\/\">mgorny-dev-scripts<\/a> package.<\/p>\n<p>Today&#8217;s <kbd>pkgbump<\/kbd> have come a long way from the trivial script of its first iteration. The most recent versions finally feature the feature I desired for a long time: version manipulation. This also made it possible for the script to become a complete version bumping tool rather than just a part of a larger workflow. In this post, I&#8217;d like to shortly tell the story behind the changes, and demonstrate the new options.<br \/>\n<!--more--><\/p>\n<h2>The first iteration<\/h2>\n<p>The initial version of <kbd>pkgbump<\/kbd> was quite trivial. It took two paths: source and destination. It copied the ebuild file, lowered the keywords, bumped the copyright date, updated the Manifest and run the <kbd>pkgdiff<\/kbd> (now <kbd>pkgdiff-mg<\/kbd>) tool to compare the unpacked archives. So a typical workflow would look like:<\/p>\n<pre>cdpkg svglib\r\npkgbump svglib-2.0.{1,2}.ebuild\r\nvim svglib-2.0.2.ebuild  # if necessary\r\n# test the package in another terminal\r\npkgcommit -sS . -m 'Bump to 2.0.2'<\/pre>\n<p>Obviously, there&#8217;s some duplication here, and a potential for improvement. And over time, some improvements would happen. I&#8217;ve added support for removing obsolete Python implementations from <kbd>PYTHON_COMPAT<\/kbd>, support for <kbd>PKGBUMPING<\/kbd> variable that would be used to skip unpacking crates when diffing and integration with <kbd>pkgcommit<\/kbd>, so instead of repeating the version number, you&#8217;d do:<\/p>\n<pre>pkgcommit -sS . --bump<\/pre>\n<h2>Bumping groups of packages<\/h2>\n<p>If <kbd>pkgbump<\/kbd> was sometimes cumbersome to use, that would be especially felt in groups of packages such as <kbd>dev-python\/botocore<\/kbd>, <kbd>dev-python\/boto3<\/kbd> and <kbd>app-admin\/awscli<\/kbd>. These three packages are released simulaneously, usually 5 times as week, often with slightly different version numbers. So you&#8217;d end up doing something like:<\/p>\n<pre>pkgbump botocore-1.24.{0,1}.ebuild\r\npkgbump boto3-1.21.{0,1}.ebuild\r\npkgbump awscli-1.22.{55,56}.ebuild<\/pre>\n<p>Thus, <kbd>bump-boto<\/kbd> was born. In its initial incarnation, it would take the old and new patch versions, and compute all the remaining numbers. So you&#8217;d just invoke:<\/p>\n<pre>bump-boto 0 1&gt;<\/pre>\n<p>In this initial version, I&#8217;d have to update the script whenever upstream changed the minor version number. The next incarnation would actually grab the minor version number from the repository, but would still require changes whenever the alignment between patch numbers changed.<\/p>\n<p>Eventually, I would replace the patch version arguments with an &#8220;increment&#8221;. So you&#8217;d just specify:<\/p>\n<pre>bump-boto +1<\/pre>\n<p>And it would increment the patch version of all packages by one. Of course, whenever the minor version or alignment changed, I would still have to <kbd>pkgbump<\/kbd> them manually, but <kbd>bump-boto<\/kbd> would work just fine for the subsequent release, no modifications needed.<\/p>\n<p>For a long time, this was a function unique to <kbd>bump-boto<\/kbd>, with a pretty dumb implementation. However, the period of frequent kernel updates, requiring me to keep retyping 7 version pairs, finally motivated me to make it more generic. Just imagine typing the equivalent of, twice a day:<\/p>\n<pre>bump-kernels 7.0.{12,13} 6.18.{35,36} 6.12.{93,94} 6.6.{142,143} 6.1.{175,176} 5.15.{209,210} 5.10.{258,259}<\/pre>\n<h2>Generic increments in pkgbump<\/h2>\n<p>The latest versions of <kbd>pkgbump<\/kbd> still accept two positional arguments: source and destination. However, they do not have to be filenames anymore.<\/p>\n<p>The destination can be a version number or an increment instead. So you could do either of:<\/p>\n<pre>pkgbump gentoo-kernel-7.0.12.ebuild 7.0.13\r\npkgbump gentoo-kernel-7.0.12.ebuild +1<\/pre>\n<p>What about the minor version changing? The increment can be followed by one or more version components, in which case the final components are set to the specified value, and the one preceding them is incremented. So you can do either of:<\/p>\n<pre>pkgbump gentoo-kernel-7.0.12.ebuild 7.1.0\r\npkgbump gentoo-kernel-7.0.12.ebuild +1.0<\/pre>\n<p>Or:<\/p>\n<pre>pkgbump gentoo-kernel-7.0.12.ebuild 7.1.2\r\npkgbump gentoo-kernel-7.0.12.ebuild +1.2<\/pre>\n<p>The source can be a pattern instead, in which case the script finds the ebuild with the highest version number matching it:<\/p>\n<pre>pkgbump 'gentoo-kernel-7.0.*.ebuild' +1\r\npkgbump 'gentoo-kernel-6.18.*.ebuild' +1<\/pre>\n<p>At this point, I could make the arguments optional. If source is omitted, <kbd>'*.ebuild'<\/kbd> is assumed. If destination is omitted, an increment of <\/kbd>+1<\/kbd> is assumed. So the following are all valid:<\/p>\n<pre>pkgbump 'gentoo-kernel-6.18.*.ebuild'  # dest.: +1\r\npkgbump +2  # source: '*.ebuild'\r\npkgbump 1.2.3  # source: '*.ebuild'\r\npkgbump  # source: '*.ebuild', dest.: +1<\/pre>\n<h2>More options for an integrated workflow<\/h2>\n<p>At the same time, I&#8217;ve added a few options to <kbd>pkgbump<\/kbd> to make it more suitable for an all-in-one tool. So effectively the process becomes, depending on options given:<\/p>\n<ol>\n<li>Copy the ebuild, copybump, clean it.<\/li>\n<li>Lower the keywords, unless <kbd>-s<\/kbd> \/ <kbd>--stable<\/kbd> is passed.<\/li>\n<li>Run the editor to edit the ebuild, if <kbd>-E<\/kbd> \/ <kbd>--edit-early<\/kbd> is passed.<\/li>\n<li>Update Manifest, unless <kbd>-M<\/kbd> \/ <kbd>--no-manifest<\/kbd> is passed.<\/li>\n<li>Diff the working directories, unless <kbd>-D<\/kbd> \/ <kbd>--no-diff<\/kbd> is passed.<\/li>\n<li>Run the editor to edit the ebuild, if <kbd>-e<\/kbd> \/ <kbd>--edit<\/kbd> is passed.<\/li>\n<li>Commit the changes, if <kbd>-c<\/kbd> \/ <kbd>--commit<\/kbd> is passed.<\/li>\n<\/ol>\n<p>So it would now be possible to reduce the initial workflow to a single call:<\/p>\n<pre>pkgbump -e -c<\/pre>\n<p>And that&#8217;s it! It means &#8220;pick the latest ebuild, increment the final version component by one, manifest, diff, edit and commit.&#8221;<\/p>\n<h2>Bumping kernels the easy way<\/h2>\n<p>This meant that I could finally make bumping kernels easier. The scripts are still work-in-progress, but now I can specify the branches instead of version pairs, to increment them all by one:<\/p>\n<pre>bump-kernels 7.0 6.18 6.12 6.6 6.1<\/pre>\n<p>Or I can just run the script without arguments to have it determine the available branches and bump them all:<\/p>\n<pre>bump-kernels<\/pre>\n<h2>Snippets for the future<\/h2>\n<p>One of the features of <kbd>pkgbump<\/kbd> is that it attempts to do some minimal cleanup \/ modernization of ebuilds. For example, quite early I&#8217;ve added the cleanup of old Python implementations from <kbd>PYTHON_COMPAT<\/kbd>; it&#8217;s something that&#8217;s not urgent enough to justify the noise of mass updating, but worth doing if you&#8217;re bumping the ebuild anyway.<\/p>\n<p>Lately I wanted to add another similar function: updating the deprecated <kbd>DISTUTILS_USE_PEP517<\/kbd> values. Again, this is nothing urgent; it will be required for EAPI 9 though. And similarly to <kbd>PYTHON_COMPAT<\/kbd> updates, it&#8217;s quite specific to Python. It felt that <kbd>pkgbump<\/kbd> would end up with a lot of Python-specific logic for a generic tool.<\/p>\n<p>But why restrict yourself to Python? I&#8217;m definitely open to adding other kinds of cleanup functions that people submit; except that keeping all that different logic inline feels kinda ugly. So the most recent version of <kbd>pkgbump<\/kbd> features cleanup snippets!<\/p>\n<p>The Python-specific code is gone from the tool (only the generic copyright bumping and keyword lowering logic remains in the core), and now lives in <kbd>\/usr\/lib\/pkgbump.d\/python<\/kbd>. New scripts can also be dropped into that directory, either as part of mgorny-dev-scripts, or separately packaged. And there&#8217;s <kbd>\/etc\/pkgbump.d<\/kbd> for user scripts, which can also be used to override system scripts. So if you don&#8217;t want <kbd>pkgbump<\/kbd> to be doing its Python magic, you can drop a replacement <kbd>\/etc\/pkgbump.d\/python<\/kbd>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bumping packages is one of the most common tasks of a Gentoo developer. It shouldn&#8217;t then be surprising that it is the one most asking for some kind of automation, and that the pkgbump script would be one of the first scripts to become a part of the mgorny-dev-scripts package. Today&#8217;s pkgbump have come a &hellip; <a href=\"https:\/\/blogs.gentoo.org\/mgorny\/2026\/06\/21\/pkgbump-from-a-dumb-tool-to-an-irreplaceable-helper\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;pkgbump: from a dumb tool to an irreplaceable helper&#8221;<\/span><\/a><\/p>\n","protected":false},"author":137,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[3],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/2720"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/users\/137"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/comments?post=2720"}],"version-history":[{"count":9,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/2720\/revisions"}],"predecessor-version":[{"id":2730,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/2720\/revisions\/2730"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/media?parent=2720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/categories?post=2720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/tags?post=2720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}