Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(cmd-version): add support for partial tags#1115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
MatthieuSarter wants to merge3 commits intopython-semantic-release:master
base:master
Choose a base branch
Loading
fromMatthieuSarter:master

Conversation

@MatthieuSarter
Copy link

@MatthieuSarterMatthieuSarter commentedDec 8, 2024
edited
Loading

Purpose

Add a new optionadd_partial_tags to handle creation and update of partial tags for major and major.minor.

Rationale

It will simplify the CI process for project using partial tags, as it will no more require to add scripts to update those after performing a version bump with psr.

How did you test?

For now, I only did manual testing. I need more time to setup a fully working test environment and investigate the way the tests are written. That's why the PR is opened as draft for now, just so you know that I'm working on the feature and can have a first look at it. I should have some spare time at the end of the month or in january to look at the tests.

How to Verify

  • Setadd_partial_tags totrue in configuration.
  • Run psr --no-op version on a project with partial tags.
  • Verify that you got no warning about invalid version tags.
  • Verify that the output shows the commands for creating the two partial tags.

Sample output without partial tags disabled:

psr --noop version🛡 You are running in no-operation mode, because the '--noop' flag was supplied                                                                     config.py:700           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0 as as Version: '0' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0.3 as as Version: '0.3' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1 as as Version: '1' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.0 as as Version: '1.0' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.1 as as Version: '1.1' is not a valid Version                                             algorithm.py:471.2.0           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0 as as Version: '0' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0.3 as as Version: '0.3' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1 as as Version: '1' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.0 as as Version: '1.0' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.1 as as Version: '1.1' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0 as as Version: '0' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v0.3 as as Version: '0.3' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1 as as Version: '1' is not a valid Version                                                 algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.0 as as Version: '1.0' is not a valid Version                                             algorithm.py:47           WARNING  [algorithm.tags_and_versions] Couldn't parse tag v1.1 as as Version: '1.1' is not a valid Version                                             algorithm.py:47The next version is: 1.2.0! 🚀🛡 semantic-release 'noop' mode is enabled! would have written your changelog to CHANGELOG.md🛡 semantic-release 'noop' mode is enabled! would have updated versions in the following paths:    pyproject.toml    src\clitools\__init__.py🛡 semantic-release 'noop' mode is enabled! would have run the build_command poetry build🛡 semantic-release 'noop' mode is enabled!     would have run:        git add <redacted>🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git commit -m 'Version 1.2.0            Automatically generated by python-semantic-release'🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git tag -a v1.2.0 -m 'v1.2.0'🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> main🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> tag v1.2.0🛡 semantic-release 'noop' mode is enabled! would have created a release for tag v1.2.0 with the following notes: ## v1.2.0 (2024-12-08)

And the same with partial tags enabled:

psr --noop version🛡 You are running in no-operation mode, because the '--noop' flag was supplied1.2.0The next version is: 1.2.0! 🚀🛡 semantic-release 'noop' mode is enabled! would have written your changelog to CHANGELOG.md🛡 semantic-release 'noop' mode is enabled! would have updated versions in the following paths:    pyproject.toml    src\clitools\__init__.py🛡 semantic-release 'noop' mode is enabled! would have run the build_command poetry build🛡 semantic-release 'noop' mode is enabled!     would have run:        git add <redacted>🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git commit -m 'Version 1.2.0            Automatically generated by python-semantic-release'🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git tag -a v1.2.0 -m 'v1.2.0'🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> main🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> tag v1.2.0 🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git tag -af v1 -m 'v1 is v1.2.0'🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> tag v1 --force🛡 semantic-release 'noop' mode is enabled!         would have run:                            GIT_AUTHOR_NAME=semantic-release \    GIT_AUTHOR_EMAIL=semantic-release \    GIT_COMMITTER_NAME=semantic-release \    GIT_COMMITTER_EMAIL=semantic-release \    git tag -af v1.2 -m 'v1.2 is v1.2.0'🛡 semantic-release 'noop' mode is enabled!     would have run:        git push <redacted> tag v1.2 --force🛡 semantic-release 'noop' mode is enabled! would have created a release for tag v1.2.0 with the following notes: ## v1.2.0 (2024-12-08)

@codejedi365codejedi365 marked this pull request as ready for reviewDecember 8, 2024 16:01
@codejedi365codejedi365 marked this pull request as draftDecember 8, 2024 16:56
@codejedi365
Copy link
Contributor

codejedi365 commentedDec 8, 2024
edited
Loading

@MatthieuSarter, thanks for all the hard work you have put in here. It looks good and I was not thinking about this kind of feature or how to implement. One suggestion would be to change the configure name to a longer more descriptive namerolling_partial_tags oradd_partial_tags because I personally didn't understand whatrolling_tags were at first glance. I think the crucial difference is that they are partial version numbers and its a bit less important to denote them as rolling as PSR's activity is only for the current version. But we will bake in the ability for--force which enables PSR to work over in a repeating fashion.

Thanks for adding the descriptions and doc updates and timeline for testing rather than just passing it to me to complete (surprising how many people do this). To help understand the test suite, note there is the tests/ directory that is split into unit & e2e folders and how to run the test suite is documented inContributing.rst or you can check out the.github/workflows/validate.yml configuration. I am more critical about the e2e than unit tests since that takes a lot of the guess work out of will it work for our thousands of users (3.8K repos on GitHub, average of 440K downloads/month). I think yours does fit mostly in the e2e test bucket anyway because you are depending on a configuration setting and then proving that tags are created or updated. With this said, e2e tests generally use our built in repo fixtures, which build actual test repositories of various branching, merging, and release strategies, then the tests apply configuration settings viaupdate_pyproject_toml fixture and then execute the main entrypoint with the correct subcommand's cli args passed to click'sCliRunner. Since your changes are all around theversion subcommand, they should be added into thetests/e2e/cmd_version/ folder, possibly put into its own file astest_version_bump is quite large. As you will see any tests intest_version_bump assert that all actions have taken place so we know that this variation doesn't have a weird side impact on something we didn't consider. You should be considering at a minimum, a test for when previous partial version tags don't exist and then when they do previously exist. I don't think your change will need to test all repo variants but probably a few as it would be worth considering if prereleases exist or a prerelease is requested whenadd_partial_tags is enabled. Likely this would berepo_w_trunk_only_*_commits,repo_w_trunk_only_n_prereleases_*_commits (note all repo fixtures start withrepo_*). One trick to testing is to capture the expected results from the prebuilt repos and then as part of the setup, reverse the repo down to the previous commit or tag and then validate that PSR versioned appropriately. You can also extract commits and version lists from the build definition through theget_ fixtures.

Recommendadd_partial_tags be set toFalse as the default since we would be adding this as a new user configured feature intov9. Your test would set this value true during setup. You shouldn't need a new repo fixture just apply previous partial version tags manually after usingget_version_strings_from_repo_def(repo_result["definition"]) for updating existing partial tags test.

It is probably also worth noting that arepo_* fixture returns aBuiltRepoResult object which includes an openedRepo object and the resulting build steps (with commit hashes). Repo fixtures also change the current directory to a temporary pytest folder so you can assume that you are in the fake project repo when runningrepo.git.* commands. For speed of iterative testing, we have a smart/dynamic cache of each repo fixture's start state in the.pytest_cache/d/psr-cached-repos/ and their definitions in.pytest_cache/v/psr/repos/. These are copied (rather than rebuilt) into the pytest temporary test directory so that each test is isolated from each test's git actions.

I did swap your PR to ready for review to see how your changes currently evaluate in the CI and nothing failed so you effectively added functionality without breaking anything (that we know of) which is awesome. Now we would just be looking for tests that exercise your new functionality (tag creations & updates) and any new ways that it would impact other non-default settings or environments. Good job so far!

@MatthieuSarter
Copy link
Author

@codejedi365 , thanks for your explanation, it will be helpfull to help me add tests :)

Regarding the name of the option, I used this name as it is the name I commonly encountered on other projects for naming such tags, especially in the Docker world (see here for instance:https://docs.vmware.com/en/VMware-Tanzu-Application-Catalog/services/main/GUID-apps-tutorials-understand-rolling-tags-containers-index.html ), but if you prefer an other terminology, I'm totally fine with it. Among your two suggestions, I would prefer rolling_partial_tags, which would both indicate that it enable rolling tags and that those tags are based on partial version (as opposed to rolling tags like "latest", without any version infos).

@codejedi365
Copy link
Contributor

Regarding the name of the option, I used this name as it is the name I commonly encountered on other projects for naming such tags, especially in the Docker world (see here for instance:https://docs.vmware.com/en/VMware-Tanzu-Application-Catalog/services/main/GUID-apps-tutorials-understand-rolling-tags-containers-index.html ), but if you prefer an other terminology, I'm totally fine with it. Among your two suggestions, I would prefer rolling_partial_tags, which would both indicate that it enable rolling tags and that those tags are based on partial version (as opposed to rolling tags like "latest", without any version infos).

I appreciate the insight and thought considered for the name. As I continued writing my response I think I have settled on theadd_partial_tags as it best describes what it is doing to include the verb and subject. Adding the adjective ofrolling conflates with the result and impact of one possible activity or use case but is not ultimately what PSR is doing. The setting needs to be descriptive of the action PSR is doing only. I highly recommend though that your documentation of the setting value include the example in detail of the use case for rolling tags as for a primary reason of why to use the setting. This is where the resulting impact really can used to inform users of new possibilities.

MatthieuSarter reacted with thumbs up emoji

@codejedi365
Copy link
Contributor

codejedi365 commentedDec 9, 2024
edited
Loading

@MatthieuSarter, Thinking about the tests a little more, I think it will be important to verify the following situations:

  1. [Tag Creation] Very first release on a new project withadd_partial_tags = true (ie.repo_w_no_tags_*_commits)
  2. [Tag Creation] Ongoing project that just decided to enable the featureadd_partial_tags = true (ie.repo_w_trunk_only_*_commits)
  3. [Tag Update] Existing partial tags on previous release, next release is a patch release, must update both major & minor (ie.repo_w_trunk_only_*_commits)
  4. [Tag Update & Create] Existing partial tags on previous release, next release is a minor release, must update the major partial tag & create new minor tag, while validating the old minor tag exists (ie.repo_w_trunk_only_*_commits)
  5. [Tag Create & Maintain] Existing partial tags on previous release, next release is a major release, must create a new major partial tag & create new minor tag, while validating the old major & minor tags still exist (ie.repo_w_trunk_only_*_commits)
  6. [Prerelease Validation] Whenadd_partial_tags = true, and a prerelease occurs, no partial tag is created? (ie.repo_w_trunk_only_n_prereleases_*_commits)
  7. [build metadata tag creation] see question below
  8. [build metadata change/tag update] no change in version but build metadata changed, partial major, minor, and patch updated.

Question: how would we consider handling a build metadata change release or just a release with build metadata? I feel like we should be creating a "partial" release from the full tag which would result in avX.X.X (fromvX.X.X+build.20241201). Either way, this situation creates the need for additional tests to characterize the expected behavior of PSR.

Note: I recommend using the fixturemocked_git_push: MagicMock to block and capture yourgit push activity and then evaluate it. You can also usepost_mocker: Mocker to capture the--vcs-release api upload as well which prevents errors as the domain's don't exist.

@codejedi365
Copy link
Contributor

the force push change was to help resolve the conflict ingitproject.py for you as I just caused that one today.

@MatthieuSarter
Copy link
Author

@codejedi365 , just a little message to tell you I didn't forgot about my commitment to add tests to this PR, but I had a tough start of the year (flu, bike crash...) so I didn't have the opportunity to work on it yet.

@codejedi365
Copy link
Contributor

codejedi365 commentedFeb 9, 2025
edited
Loading

@MatthieuSarter, thanks for the note, I was starting to debate if you would follow through as most just leave it to me to pick up where they left off. I'm glad you haven't.

I hope you feel better soon! It sounds like a rough start to 2025 for you--must be frustrating.

If you have seen the rebases, I have just been keeping what was written up to date with the latest version as stuff has refactored which should make it easier to pick up from.

@MatthieuSarter
Copy link
Author

MatthieuSarter commentedMar 19, 2025
edited by codejedi365
Loading

@codejedi365, I finally had some time to start adding tests 🙂 Not much for now, but it's a start. I used test_version_bump.py::test_version_force_level as reference, and kept only what is pertinent for tag related checks (ie. dropped the check on version update in files, release creation in VCS, etc...).

Regarding the way of handling partial tag for prerelease, I think the answer is no update of the partial tags in this cases, as the partial tags should always point to a release, not a prerelease.

For now I opted for the same behavior in case of version with build metadata, as for me those versions are not release. But I'm not used to this versioning scheme, so I'm open to discussion about this, creating/updating partial tags up to the patch level also seems to be a reasonable option in this case.

@MatthieuSarterMatthieuSarter changed the titlefeat(cmd-version): add support for rolling tagsfeat(cmd-version): add support for partial tagsMar 19, 2025
@codejedi365
Copy link
Contributor

I finally had some time to start adding tests 🙂 Not much for now, but it's a start.

@MatthieuSarter, Glad to hear!

I used test_version_bump.py::test_version_force_level as reference, and kept only what is pertinent for tag related checks (ie. dropped the check on version update in files, release creation in VCS, etc...).

I would rather you not drop the assertions in e2e tests. In a e2e test of version, they are all pertinent. As I stated above, it is on purpose to have all the checks so we know that this variation doesn't have a weird side impact on something we didn't consider.

Regarding the way of handling partial tag for prerelease, I think the answer is no update of the partial tags in this cases, as the partial tags should always point to a release, not a prerelease.

I concur that should be the outcome. We will want a test to ensure these do not move.

For now I opted for the same behavior in case of version with build metadata, as for me those versions are not release. But I'm not used to this versioning scheme, so I'm open to discussion about this, creating/updating partial tags up to the patch level also seems to be a reasonable option in this case.

Build metadata releases are official releases, they just include more specific information (generally the date). Note that the+ is the separator as opposed to the-. From the SemVer standard, build metadata is an additional labeling extension separate from pre-release but similar. The build metadata is not however taken into consideration for version precedence. After this research, I am feeling more confident we should implement partials to include patch, minor, major when set as a build-metadata. Although it is not factored into version precedence, I do think this would move the partial tags if you released a version with a new build metadata label. Example:

* 26bb37c (tag: v9.21.0+build.20250112, tag: v9.21.0, tag: v9.21, tag: v9) chore: Release 9.21.0+build.20250112* 98287c9 ci(build): change build steps (#1202)* 5093f30 (tag: v9.21.0+build.20250101) chore: Release 9.21.0+build.20250101       # <--- partials were here
MatthieuSarter reacted with thumbs up emoji

@MatthieuSarterMatthieuSarterforce-pushed themaster branch 2 times, most recently from3a1f9ca to8862eb9CompareApril 23, 2025 22:35
@codejedi365codejedi365 added confirmedPrevent from becoming stale and removed stale labelsJun 27, 2025
@MatthieuSarterMatthieuSarterforce-pushed themaster branch 2 times, most recently frome34abeb toc4fa3c4CompareJune 27, 2025 16:43
@MatthieuSarterMatthieuSarter marked this pull request as ready for reviewAugust 17, 2025 14:24
@MatthieuSarter
Copy link
Author

@codejedi365 , I think I covered all the relevant cases now, so I marked the PR as ready for review. Sorry for having taken soooo long :-(

codejedi365 reacted with thumbs up emoji

@codejedi365
Copy link
Contributor

@MatthieuSarter, thanks for the hard work, I'll try to get this integrated this weekend

@MatthieuSarter
Copy link
Author

@codejedi365 , I just updated the tests to the new e2e infrastructure, to solve the failing tests

@codejedi365
Copy link
Contributor

Oh wow, thanks! I didn't mean to leave that on you to accomplish, I have just been busy. But thanks!

Copy link

CopilotAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Pull Request Overview

This PR adds support for creating and updating partial version tags alongside full semantic version tags. When enabled, tags likev1 andv1.2 are created/updated to point to the same commit as the full version tag (e.g.,v1.2.3).

Key changes:

  • Addsadd_partial_tags configuration option (defaults tofalse)
  • Implements partial tag creation logic in the version command
  • Updates tag filtering inVersionTranslator to exclude partial tags from version resolution
  • Adds git operations support for forced tag creation and pushing

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/semantic_release/cli/config.pyAddsadd_partial_tags configuration field toRawConfig and passes it toVersionTranslator
src/semantic_release/cli/commands/version.pyImplements partial tag creation/update logic and passes config to utilities
src/semantic_release/version/translator.pyAdds partial tag filtering regex to prevent treating partial tags as version tags
src/semantic_release/version/version.pyAdds helper methods to generate partial tag names
src/semantic_release/gitproject.pyAddsforce parameter to tag and push operations for updating existing tags
docs/configuration/configuration.rstDocuments the newadd_partial_tags configuration option
tests/e2e/cmd_version/test_version_partial_tag.pyComprehensive e2e tests for partial tag functionality

💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.

self.add_partial_tags=add_partial_tags
self.from_tag_re=self._invert_tag_format_to_re(self.tag_format)
self.partial_tag_re=re.compile(
tag_format.replace(r"{version}",r"[0-9]+(\.(0|[1-9][0-9]*))?$"),
Copy link

CopilotAINov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The regex pattern does not escape thetag_format string before replacing{version}, which could lead to incorrect matching if the tag format contains regex special characters. Usere.escape() on the tag_format before replacing the placeholder, similar to how it's done in_invert_tag_format_to_re method.

Suggested change
tag_format.replace(r"{version}",r"[0-9]+(\.(0|[1-9][0-9]*))?$"),
re.escape(tag_format).replace(
re.escape("{version}"),r"[0-9]+(\.(0|[1-9][0-9]*))?$"
),

Copilot uses AI. Check for mistakes.
forpartial_taginpartial_tags:
project.git_tag(
tag_name=partial_tag,
message=f"{partial_tag} is{new_version.as_tag()}",
Copy link

CopilotAINov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

[nitpick] The tag message format could be more descriptive. Consider using a message like 'Points to {new_version.as_tag()}' or 'Tracks {new_version.as_tag()}' to clarify that this is a reference tag that may be updated in the future.

Suggested change
message=f"{partial_tag} is{new_version.as_tag()}",
message=f"Tracks{new_version.as_tag()}",

Copilot uses AI. Check for mistakes.
"--force"ifforceelse"",
],
),
).strip()
Copy link

CopilotAINov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Thefilter(None, ...) call on line 215-216 combined with.strip() on line 233 will cause issues with the noop output formatting. The filter removes empty strings but the unpacking operator* on line 219 can still produce an empty string element (line 227), and when that's joined it creates extra spaces. The empty string on line 227 should be removed since the filter is now used.

Copilot uses AI. Check for mistakes.
)

# Modify the pyproject.toml to remove the version so we can compare it later
pyproject_toml_before.get("tool", {}).get("poetry").pop("version")# type: ignore[attr-defined]
Copy link

CopilotAINov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This line can raise an AttributeError if 'poetry' is None or not a dict. Use safer chaining with.get('poetry', {}) to handle cases where the poetry section might not exist.

Suggested change
pyproject_toml_before.get("tool", {}).get("poetry").pop("version")# type: ignore[attr-defined]
pyproject_toml_before.get("tool", {}).get("poetry", {}).pop("version",None)# type: ignore[attr-defined]

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

Copilot code reviewCopilotCopilot left review comments

Assignees

No one assigned

Labels

confirmedPrevent from becoming stale

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@MatthieuSarter@codejedi365

[8]ページ先頭

©2009-2025 Movatter.jp