Skip to content
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

Improving Releases, tags and handling of the VERSION file #44

Closed
zsliu98 opened this issue Aug 30, 2023 · 11 comments · Fixed by #65
Closed

Improving Releases, tags and handling of the VERSION file #44

zsliu98 opened this issue Aug 30, 2023 · 11 comments · Fixed by #65
Labels
documentation Improvements or additions to documentation

Comments

@zsliu98
Copy link
Contributor

zsliu98 commented Aug 30, 2023

When I want to release a new version, I have to:

  • update the VERSION file
  • git add & commit
  • git tag & git push --tags

If I create the VERSION file from git tags, I can save myself from the first step:

    - name: Get lastest tag
      uses: "WyriHaximus/github-action-get-previous-tag@v1"
      id: get-latest-tag
      with:
        fallback: 0.0.0

    - name: Setup Environment Variables
      shell: bash
      run: |
        echo "${{steps.get-latest-tag.outputs.tag}}" > VERSION
        VERSION=$(cat VERSION)
# ......

However in this case I have to make sure that the tags are in the format "-.-.-" What do you think about this?

See this workflow as an example.

@sudara
Copy link
Owner

sudara commented Aug 30, 2023

When I want to release a new version

Yes, I find this process very awkward as well. I'd like to improve it, thanks for engaging!

If I create the VERSION file from git tags

Ok, so this keeps the VERSION file (which keeps it visible and coupled to the code, which is great). When you want to increment, you specify the new tag (with the desired version) and push (still has to have a commit attached to it).

Er, wait, does the new VERSION file get committed anywhere?

Maybe instead of github-action-get-previous-tag, we could just have another build job that runs before everything, catches the tag changing via if: contains(github.ref, 'tags/v') and writes + commits the VERSION?

@sudara
Copy link
Owner

sudara commented Aug 30, 2023

Actually, maybe that's still "backwards" as it doesn't fix a few other pain points for me.

The friction I'd really love to remove is the whole "have to create a tag AND a commit AND push to trigger a release".

I often end up getting the repo ready for release, but then I have to add a fake / dumb commit to go with the tag. Or I forget to bump VERSION and it's going to release with the old VERSION.

So I'd love to bump VERSION locally. For example, I get to work on a bumped VERSION 2.1 locally — and when I finally merge into main and push, it releases.

So maybe the best solution (for me at least) would be a release action that doesn't trigger on a pushed tag, but triggers when it notices the VERSION file change, and then auto-tags that for me.

That way I don't have to care about juggling git tags — all I have to do is bump VERSION to release.

@sudara sudara changed the title Use tag to create the VERSION file Improving Releases, tags and handling of the VERSION file Aug 30, 2023
@zsliu98
Copy link
Contributor Author

zsliu98 commented Aug 30, 2023

Er, wait, does the new VERSION file get committed anywhere?

No. The content of the VERSION file will be replaced by the latest tag when you execute the action. But the change will not get committed (of course you can commit it if you like).

I have three actions here:

  • Bump tag: bump and push a new tag, on dispatch
  • Test: build the plugin and test it several times, on push, pull request and dispatch
  • Release: build the plugin, test it, and release it if triggered with tag, on dispatch

A drawback is that the VERSION file will not be consistent with the latest tag. Since I do not care about the version when building locally, it is not an issue for me.

In such a way, I am able to create a new release without doing anything locally. For example, I use Renovate to update submodules for me. A pull request (e.g. an update on JUCE master) from Renovate will first be verified by Test. After that, I merge it, Bump tag, and Release a new version.

@sudara
Copy link
Owner

sudara commented Aug 30, 2023

Thanks for the details!

It sounds like you have a good flow. For Pamplejuce, I think it be too confusing for others to have the local VERSION not updated / irrelevant / out of sync. Especially if they are including the major version as a part of their product names (which lets people cut breaking changes as new versions without impacting older projects). In that case, we need to be able to work with different versions locally.

You’ve motivated me to look into resolving the friction though — thanks! Right now I think “pushing an updated VERSION” is probably most compatible with people’s expectations. Sounds like I should try out a few methods, including yours, to see what feels best.

@sudara
Copy link
Owner

sudara commented Aug 31, 2023

"pushing an updated VERSION creates tags and triggers release" also has a couple drawbacks, I think:

  1. local git tags out of sync (not sure if relevant)
  2. People can't cut a version of the same release twice, meaning if you push VERSION 1.1.0 and the build fails, you'd need to again push the fix with a changed VERSION file, effectively needing to bump to 1.1.1 to get the release out.

I guess the "cut releases based on pushing git tags" also has the same issue as #2, I guess. The benefit in both cases is that you only have to do 1 thing to cut a release, and it's "hygienic."

@zsliu98
Copy link
Contributor Author

zsliu98 commented Aug 31, 2023

local git tags out of sync (not sure if relevant)

Yes, that's correct. If I rely on the VERSION file, the git tags will be out of sync. If I rely on the git tags, the VERSION file will be out of sync. Although git will notify me when I push anything to remote, this inconsistency might raise problems.

People can't cut a version of the same release twice, meaning if you push VERSION 1.1.0 and the build fails, you'd need to again push the fix with a changed VERSION file, effectively needing to bump to 1.1.1 to get the release out.

In such a case, I would delete the tag 1.1.0 remote & local, fix, commit, and tag it again with 1.1.0. Assume that sufficient tests will be carried out before updating VERSION / tagging a repo, such cases should be rare.

@sudara
Copy link
Owner

sudara commented Aug 31, 2023

The bump tag action you are using looks interesting. I like that it enforces a PR flow and increments with semver — definitely up my alley!

Assume that sufficient tests will be carried out before updating VERSION / tagging a repo, such cases should be rare.

My experience is usually that CI catches and does a lot of extra things that the local environment doesn't, so I often end up with one platform having some issues even after running tests locally. However, I'm also developing Pamplejuce and constantly doing things like updating parts of it, so I probably run into more random failures at different parts of the build/release chain that most people would.

So I guess the ideal is "only tag once it's green in CI and passed QA." I'm definitely guilty of just pushing tags to my private repos hoping it'll work out. In a way I wish there was a way to manually "release" on a normal green CI commit...

@sudara
Copy link
Owner

sudara commented Aug 31, 2023

Thanks (again!) for providing your perspective.

I'm going to summarize the options with pros/cons for Pamplejuce proper. I'll get a few more people using it to weigh in before making a decision I think.

Git Tag-driven

Workflow

Create a git tag and push to trigger a release:

git commit -m "Releasing v0.0.2"
git tag v0.0.2
git push --tags 

Pros

  • One source of authority on version (the tag)
  • Releases get built when the tag is incremented
  • Creates a nice git history
  • Possible (but annoying) to re-release the same version, by deleting tag, committing fixes, re-tagging
  • No need for VERSION file

Cons

  • Requires being familiar/comfy with git tags
  • Commit/Tag/Push is a little bit of work/friction
  • No locally sync'd VERSION. We could fix this by reading in the git status via CMake. The tag itself isn't what's checked out most of the time, so I guess it would set the version to the "highest numbered tag available"

Variants

On GitHub Actions dispatch, use github-tag-action to auto bump the version and push a new tag on merge. This requires using PRs to manage the workflow (which a lot of solo devs may not want bother with).

VERSION file driven

  • Edit the VERSION file and stick the new version in there. GitHub Actions runs the release workflow when a change is detected.

Pros

  • Very easy mental model
  • One source of authority on version, which is local-first
  • The version is already propagating to the local plugin/app
  • Can still auto-tag in GitHub Actions to provide nice history

Cons

  • No mechanism to re-release the same exact version. You would have to bump the patch level (which is maybe appropriate? But might be noisy)
  • If auto-tagging in GA, would have pull locally to get tags in sync

@sudara sudara added the 1.0 label Aug 31, 2023
@kriskeillor
Copy link

kriskeillor commented Sep 1, 2023

@sudara
The last con for tags is the worst one

The first con for VERSION is not really a con at all; perhaps noisy, but it's relevant noise

The second con for VERSION also seems not hugely significant, either - you just add --tags when you are pulling new code?

Sounds like this VERSION file idea is a great one. Having GA automate tag naming seems like a pro, too.

@zsliu98
Copy link
Contributor Author

zsliu98 commented Sep 1, 2023

find_package(Git)

if(GIT_EXECUTABLE)
  # Generate a git-describe version string from Git repository tags
  execute_process(
    COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
    OUTPUT_VARIABLE GIT_DESCRIBE_VERSION
    RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE
    OUTPUT_STRIP_TRAILING_WHITESPACE
    )
  if(NOT GIT_DESCRIBE_ERROR_CODE)
    set(FOOBAR_VERSION ${GIT_DESCRIBE_VERSION})
  endif()
endif()

if(NOT DEFINED FOOBAR_VERSION)
  set(FOOBAR_VERSION 0.0.0)
  message(WARNING "Failed to determine VERSION from Git tags. Using default version \"${FOOBAR_VERSION}\".")
endif()

string(REPLACE "v" "" FOOBAR_VERSION_WITHOUT_V "${FOOBAR_VERSION}")

file(WRITE VERSION "${FOOBAR_VERSION_WITHOUT_V}")

After some search on Google, here is the best solution I can think of currently (ref: here). Perhaps you can add it as an optional Cmake module so that people can choose whether to use it!

It is compatible with (a previous version? of) Pamplejuce except for the sequence of the GA steps. I have to adjust it so that CMake is able to generate the VERSION file earlier. It syncs the VERSION file with the latest git tag (hopefully without any character other than "v").

@sudara
Copy link
Owner

sudara commented Sep 4, 2023

@kriskeillor Thanks for weighing in. I agree with you, it seems most direct and clear to have VERSION be the authoritative source.

@zsliu98 Thanks again! Appreciate the effort here. Another route would be to handle the tagging behind the scenes and just control the version from commits ala https://github.com/marketplace/actions/tag-version

After speaking with a few others, I'm leaning towards the VERSION file route, as its easiest and has the most flexible mental model (esp. for those new to git, those don't care about tagging, or people like me, who like to bump the version locally so they can get the new version into shape).

@sudara sudara added documentation Improvements or additions to documentation and removed 1.0 labels Nov 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants