Skip to content

README: Versioning policy #39

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

README: Versioning policy #39

wants to merge 1 commit into from

Conversation

langston-barrett
Copy link
Contributor

Fixes #36.

@kquick
Copy link
Member

kquick commented Apr 17, 2025

Although this is the standard practice of the repository referenced, I have some significant concerns about this methodology. Although fundamentally, a branch base, a branch HEAD, and a tag are all references to a specific commit, the git tool only expects one of those to be moved during normal workflows. It's possible to manage a repository in the way described, but it means knowing to use additional flags and creates synchronization headaches with downstream.

  1. In the "developer" section of the documentation, the suggestion is:
    git tag --annotate vX.Y.Z --message vX.Y.Z
    git push origin vX.Y.Z
    
    However, this should also include advisements to push the head of the current branch, otherwise the tag will contain commits not present on the development branch: o..o(HEAD)..o..o(vX.Y.Z)
  2. In the above, the more macro-version tags should also be created and pushed (vX.Y and vX). Thus, I think the recommendations for the developer documentation (should this methodology be used) should be:
    git tag --annotate vX.Y.Z --message vX.Y.Z
    git tag --annotate vX.Y --message vX.Y --force-with-lease
    git tag --annotate vX --message vX --force-with-lease
    git push origin    # to update HEAD of the current branch
    git push origin vX.Y.Z
    git push origin vX.Y  --force-with-lease
    git push origin vX --force-with-lease
    
  3. I'd suggest --force-with-lease rather than --force because otherwise you have the risk of overwriting/losing commits pushed by another person at around the same time. And since there are multiple pushes with leases occurring, there's a miniscule but very real possibility that you will get through a couple of these, then someone else will get theirs in, and your subsequent attempts will fail. Now things are a mess and you need to explicitly coordinate with the other person to ensure all changes get pushed and all tags are updated to the proper locations.
  4. The above is complicated, with extra flags and commands which don't handle multi-developer workflow coordination.
  5. It's possible for developers to get out of sync relative to force pushed flags. The instructions in this commit need to be updated with additional non-standard git commands for the other developers to stay in sync. As an example, I created a blank repository on github:
$ git clone [email protected]:kquick/tag
Cloning into 'tag'...
warning: You appear to have cloned an empty repository.
~ $ cd tag
~/tag $ echo commit1 > info
~/tag $ git add info
~/tag $ git commit -m 'commit1'
[main (root-commit) 02aee56] commit1
 1 file changed, 1 insertion(+)
 create mode 100644 info
~/tag $ git tag --annotate v1.0.0 -m v1.0.0
~/tag $ git tag --annotate v1.0 -m v1.0
~/tag $ git tag --annotate v1 -m v1
~/tag $ git push origin v1.0.0
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 32 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 414 bytes | 414.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), done.
To github.com:kquick/tag
 * [new tag]         v1.0.0 -> v1.0.0
 * [new tag]         v1 -> v1
 * [new tag]         v1.0 -> v1.0
~/tag $ cd ..
~ $ git clone [email protected]:kquick/tag tag2
Cloning into 'tag2'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 2), reused 6 (delta 2), pack-reused 0 (from 0)
Receiving objects: 100% (6/6), done.
Resolving deltas: 100% (2/2), done.
~ $ cd tag
~/tag $ echo commit2 > info
~/tag $ git add info
~/tag $ git commit -m 'commit2'
[main 39e8c44] commit2
 1 file changed, 1 insertion(+), 1 deletion(-)
~/tag $ git tag --annotate v1.0 -m v1.0 --force
Updated tag 'v1.0' (was 62180ac)
~/tag $ git tag --annotate v1 -m v1 --force
Updated tag 'v1' (was c46f509)
~/tag $ git push origin v1 --force
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 32 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 365 bytes | 365.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:kquick/tag
 + c46f509...7c5e4b6 v1 -> v1 (forced update)
~/tag $ cd ../tag2           # NOTE: I forgot to push all the tags!
~/tag2 $ git pull
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 0), reused 4 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (4/4), 345 bytes | 345.00 KiB/s, done.
From github.com:kquick/tag
 * [new branch]      main       -> origin/main
~/tag2 $ git show v1
tag v1
Tagger: Kevin Quick <...>
Date:   Thu ...

v1

commit 02aee564ea27dffda7e90ff8e28b88cd4fe066cd (tag: v1.0.0, tag: v1.0, tag: v1)
Author: Kevin Quick <...>
Date:   Thu ...

    commit1
...
~/tag2 $

Oh no: my development in the tag2 directory sees a very different version of where the v1 tag is!

~/tag2 $ cd ../tag
~/tag $ git show v1
tag v1
Tagger: Kevin Quick <...>
Date:   Thu ...

v1

commit 39e8c44a40aaf80b3f6a3029c1ff886f7eb3d10c (HEAD -> main, tag: v1.0, tag: v1, origin/main)
Author: Kevin Quick <...>
Date:   Thu ...

    commit2
...

In order to fix this, the developer in tag2 needs to explicitly request tag updates:

~/tag $ cd -
~/tag2 $ git pull --tags
From github.com:kquick/tag
 ! [rejected]        v1         -> v1  (would clobber existing tag)
~/tag2 $

Oops, more --force flags are needed:

~/tag2 $ git pull --force --tags
From github.com:kquick/tag
 t [tag update]      v1         -> v1
Already up to date.
~/tag2 $ git show v1
tag v1
Tagger: Kevin Quick <...>
Date:   Thu ...

v1

commit 39e8c44a40aaf80b3f6a3029c1ff886f7eb3d10c (HEAD -> main, tag: v1, origin/main)
Author: Kevin Quick <...>
Date:   Thu ...

    commit2
~/tag2 $

If this methodology for updating tags is used, the documentation in this PR should be updated to include the above info.

HOWEVER:

What I would actually recommend is using a methodology more aligned with git's standard functionality: create a branch for each vX and vX.Y and update accordingly. This still requires some extra commands, but only for the main developer, and they are fairly standard commands most git users should be familiar with that don't cause issues for downstream developers.

$ git clone ...
# updates made (assumed: changes synchronized back to main via direct commits or PR merges)

To make a major version release:

$ git checkout -b vX
$ git checkout -b vX.Y vX
$ git tag --annotate vX.Y.Z -m vX.Y.Z
$ git push origin --branches
$ git push origin --tags

Or to make a minor release:

$ git checkout vX
$ git merge --commit vX.last-Y  # just to make sure: should do nothing if process has been followed
$ git checkout -b vX.Y
$ git merge --commit main
$ git tag --annotate vX.Y.Z -m vX.Y.Z
$ git checkout vX
$ git merge --commit vX.Y
$ git push origin --branches
$ git push origin --tags

Or a bugfix release

$ git checkout vX.Y
$ git merge --commit main
$ git tag --annotate vX.Y.Z -m vX.Y.Z
$ git checkout vX
$ git merge --commit vX.Y
$ git push origin --tags

And as the ~/tag2 developer:

$ git pull

Advantages:

  • simple for the developers staying up to date and not processing releases,
  • a standard git workflow, with no unsafe flags,
  • matches git tool normal functionality that does not need extra flags to be specified in multiple places to avoid problems
  • normal synchronization workflow between developers.

@langston-barrett
Copy link
Contributor Author

Thank you for the thoughtful and thorough response! I completely understand and share your concerns. I think it's quite unfortunate that GitHub recommends moving tags.

I agree with all of your proposed edits. If we decide to go forward with the tag-moving methodology, I'll incorporate them (and a statement describing the concerns with this methodology) into separate developer documentation.

This question has been extensively discussed in the broader GitHub Actions community, see here: actions/toolkit#214

Although this is the standard practice of the repository referenced

Just to be clear: I believe that this is the standard practice across the GHA ecosystem. It is certainly in use on actions/checkout and actions/cache and haskell-actions/setup.

I personally don't really know what to make of the situation. I'm generally in favor of adopting the standard practice of the target ecosystem as it avoids unforseen consequences (if it works for actions/checkout, then presumably it will work for us). However, I completely agree with the drawbacks pointed out by @kquick and others on the linked issue.

@langston-barrett
Copy link
Contributor Author

langston-barrett commented Apr 17, 2025

Some more links:

Two other thoughts:

We could simply choose not to offer "major-only" refs like vX. Consumers would just specify the full version of the release they want to use. This has the advantage of more reproducible CI builds. Updating can be handled by Dependabot (you could even configure Dependabot to only bump the haskell-ci action if you wanted). This would obviate making a decision on an approach here, and save us some time.

We might be able to set up some automation, either a script or another GitHub action, to perform the sequence of tag/branch updates. actions/checkout has some automation here, and there is a work-in-progress action dedicated to this purpose.

@RyanGlScott
Copy link
Contributor

I don't have a strong opinion here. Given that we will likely be the only users of these actions, I think we should pick whichever convention makes the most sense to us. I would be fine with specifying the full version of a particular release if we needed to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Document how to release new versions
3 participants