Skip to content

uv.lock is out of sync after running a release #1125

Closed

Description

@JonZeolla

Question

How can I keep my uv.lock file in sync with my other files like pyproject.toml?

I reviewed #1097 but my situation is different in two important ways:

  1. I'm not building using psr; I have my own custom logic for that which is handled after the release is cut.
  2. We use the GitHub Action and my uv uses private indexes so I would need to find a way to pass numerous secrets / specially formatted env vars into the runtime using the prior approach.

Suggestion

When I update my uv.lock, the change looks something like this:

 [[package]]
 name = "example"
-version = "0.7.0"
+version = "0.10.0"

There's no need for any other complex locking behavior (hash pinning, etc.).

It seems like we could be able to specify this version update with a version_toml configuration or similar. However the problem is that we need to match on the [[package]] where name = "example" and then update the corresponding version field. I considered version_variables for this, which shows the potential to work but feels a bit hacky.

Configuration

Semantic Release Configuration
[tool.semantic_release]
major_on_zero = false
version_toml = [
    "pyproject.toml:project.version",
]
version_variables = [
    "src/example/__init__.py:__version__",
]
changelog_file = "CHANGELOG.md"
commit_parser = "angular"
commit_author = "Example <[email protected]>"
commit_message = "chore(release): example {version}"
tag_format = "example=={version}"

[tool.semantic_release.branches.release]
match = "main"

[tool.semantic_release.publish]
dist_glob_patterns = ["path_relative_to_root/example/dist/*"]
upload_to_vcs_release = true

[tool.semantic_release.remote]
name = "origin"
type = "github"
ignore_token_for_push = false
insecure = false

Activity

  1. codejedi365 commented on Dec 16, 2024

    @codejedi365
    Contributor

    I reviewed #1097 but my situation is different in two important ways:

    1. I'm not building using psr; I have my own custom logic for that which is handled after the release is cut.

    2. We use the GitHub Action and my uv uses private indexes so I would need to find a way to pass numerous secrets / specially formatted env vars into the runtime using the prior approach.

    The GitHub Action is only a convenience wrapper. It is not designed to fit all use cases. If it is the case where the GitHub Action is not convenient then just install PSR as a Python package into your CI environment and call it directly from the shell. If you decide to use the build command then we provide build_command_env as a specification of the env variable names you want preserved into the build command shell environment.

    Since you are not wanting to use the PSR build command, I would just run your uv lock as a separate step. In this case you would probably need to run the following:

    # PSR stamps version into files (nothing else)
    # don't build the changelog (especially in update mode)
    semantic-release -v version --skip-build --no-commit --no-tag --no-changelog
    
    # run UV lock as pyproject.toml is updated with the next version
    uv lock
    
    # stage the lock file to ensure it is included in the PSR commit
    git add uv.lock
    
    # you may need to reset your working directory here
    # if anything weird happens as PSR stamps for the 2nd time
    # git checkout .
    
    # run PSR fully to create release
    semantic-release -v version --skip-build

    It seems like we could be able to specify this version update with a version_toml configuration or similar. However the problem is that we need to match on the [[package]] where name = "example" and then update the corresponding version field. I considered version_variables for this, which shows the potential to work but feels a bit hacky.

    We don't have an advanced toml parser to consider multiple keys to point to the correct value. Maybe we could if we had a clear way of defining it in the configuration but I don't know how that would work best.

    version_variables is only a complex Regular Expression replace and likely will have undesirable effects on your uv.lock file as there are multiple version = specifications which will all be replaced regardless of which package it is related to.

    Neither of these current implementations will work for your use case. The best recommendation I have is the double PSR call above.

  2. added and removed
    triagewaiting for initial maintainer review
    on Dec 16, 2024
  3. JonZeolla commented on Dec 16, 2024

    @JonZeolla
    ContributorAuthor

    Makes sense. I will move off the GitHub action for now. Thanks for the quick reply @codejedi365

    As far as a more complex solution I may be willing to contribute something if we can agree on a design that makes sense. My first thought was some sort of per line predicate for the toml config? We could make it optional and invoked with a second : perhaps? Not sure exactly what it would look like and if you'd even be open to that. Or if people may already have a second : in those lines which would make it require a different approach

  4. JonZeolla commented on Dec 16, 2024

    @JonZeolla
    ContributorAuthor

    Regarding the regex my thought was to handle multiple lines to cover that issue about hitting other versions. I do still believe it'd be fragile and not sure if multiline would work; I only briefly looked at the implementation

  5. added
    triagewaiting for initial maintainer review
    and removed on Dec 16, 2024
  6. JonZeolla commented on Dec 16, 2024

    @JonZeolla
    ContributorAuthor

    @codejedi365 thanks; I confirmed this all works e2e. Closing this for now; you're welcome to reopen if you want to pursue the enhancement

  7. codejedi365 commented on Dec 17, 2024

    @codejedi365
    Contributor

    @JonZeolla, wow that's great! Glad it all worked out well, thanks for replying!

    I will think on the solutions for a while. Thanks for putting some thought to it. I did have plans to bring back some functionality for custom regex but I agree most are fragile for a lock file. If we move to a plugin model at some point in the distant future, I could foresee a plugin that specially handled a complex toml file or just uv.lock in general. Just at this time I cant see adding it to the baseline especially when uv is below 1.0.0.

  8. codejedi365 commented on Dec 17, 2024

    @codejedi365
    Contributor

    I will move off the GitHub action for now.

    With the two executions of PSR, each one could be the action as the cli parameters are all available via yaml with: values. I was probably a bit too harsh about its flexibility for your problem set and didn't require the switch. The downside I was thinking of is that uv is not installed in the container so adding the uv lock command in the build command requires install, all for the fact you weren't using a build command. But none of that really mattered in your case. You just needed it to stop at the stamp action before completing the release.

    With that said, eventually #692 will be completed and we will move to a composite action which would eliminate the container build time (and restricted Python version) on GitHub Actions we impose right now. You would still have to do the two executions and uv lock in between but at least it would run faster. Future plan is to also add some caching of parsed commits so that the second execution is much faster when you have large commit histories.

  9. JonZeolla commented on Mar 11, 2025

    @JonZeolla
    ContributorAuthor

    @codejedi365 It looks like this flow broke in 8906d8e; if I do a --no-commit run first, then a --commit run, it won't commit the prior changes (as defined by the version_variables and version_toml) unless I intentionally stage them myself. Is that the new expected flow?

  10. JonZeolla commented on Mar 11, 2025

    @JonZeolla
    ContributorAuthor

    My guess is that it's this and this. Since we stage the changes before running the --commit, the new_content is the same as self.content (in the working tree), so it returns None.

    Then, that doesn't add the file path to paths here, which means it is not added to files_with_new_version_written here:

    files_with_new_version_written = apply_version_to_source_files(
    repo_dir=runtime.repo_dir,
    version_declarations=runtime.version_declarations,
    version=new_version,
    noop=opts.noop,
    )
    all_paths_to_add.extend(files_with_new_version_written)

    And then finally, it isn't staged and commit here:

    if commit_changes:
    project.git_add(paths=all_paths_to_add, noop=opts.noop)
    # NOTE: If we haven't modified any source code then we skip trying to make a commit
    # and any tag that we apply will be to the HEAD commit (made outside of
    # running PSR
    try:
    project.git_commit(
    message=commit_message.format(version=new_version),
    date=int(commit_date.timestamp()),
    no_verify=no_verify,
    noop=opts.noop,
    )
    except GitCommitEmptyIndexError:
    log.info("No local changes to add to any commit, skipping")

  11. codejedi365 commented on Mar 12, 2025

    @codejedi365
    Contributor

    Is that the new expected flow?

    Yes and no. Yes, it was intentional to better detect if we actually made changes to files before staging them. Separately, I did not think about the consequence to your workflow that was expecting to apply versions again to specific files. So no, it was not intentional to cause this workflow to require a separate git add step. I opened #1211 to document this issue since it doesn't actually have anything to do with the uv.lock file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions