Skip to content
This repository has been archived by the owner on Oct 5, 2018. It is now read-only.

Support concurrent development in auto versioning #115

Closed
dlethin opened this issue May 14, 2015 · 17 comments
Closed

Support concurrent development in auto versioning #115

dlethin opened this issue May 14, 2015 · 17 comments
Milestone

Comments

@dlethin
Copy link

dlethin commented May 14, 2015

I've been spending some time on-and-off when I can find it walking through your plugin and looking to see if I can extend and contribute back adding support for version strategies that are derived from the maximum version -- either from a tag or from a branch.

Taking git flow as an example, if I am on the develop branch and my derived version is 1.0.0-SNAPSHOT, if I were to create a release/1.0.0 branch, then when I return to the develop branch, I want the derived version to be 1.1.0-SNAPSHOT (based on a policy that assumes MINOR updates.

I took a stab at starting to implement something like this in a fork of the codebase and have some of this working. It involves modifying SemVerStrategyState class to include in addition to having a NearestVersion, having a MaxVersion as well.

In order to get some feedback with the goal of hopefully getting this into the codebase, is it better to just provide you a link in this issue to a fork of the repo, or should I submit a pull request, even though the current solution might not be mature enough. Do you have a preference?

Once I have the field in place in the Strategy state, I can then build some PartialStateStrategies similar to what's in Strategies class. There is then the question of whether they should be added to Strategies, or possibly create a new plugin similar to the release-opinion -- possibly called release-gitflow, and have perhaps a GitFlowStrategies file.

Any insights into how you might solve and decompose this problem would be greatly appreciated.

Cheers.

Doug

@ajoberstar
Copy link
Owner

First thing I'll mention is that I'm partway through a more general implementation of the version inference portion of gradle-git in semver-vcs, though I intend it to have more defined semantics (which should still line up with your use case). The goal is to support more than just Git and more than just Gradle.

I don't know if I'm far enough along to address how it would work in the new library, but I plan to keep compatibility with the current version for a while at least. So we can move forward either way (targeting the new or the old).

Back to your specific question... I'm kind of surprised that the current plugin wouldn't support your use case. The only requirement would be that you merge the commit tagged as 1.0.0 back into the develop branch. I haven't used GitFlow personally, but that was my assumption of how it would work. Can you provide some insight into the gap between the plugins abilities and your goals?

Regarding the max concept... Assuming I'm interpreting it correctly, the "max" would be the tagged version with the highest semantic versioning precedence, regardless of whether it's an ancestor of the current HEAD or not. That seems like it wouldn't allow you to do maintenance (as soon as 2.0.0 is released, you can't infer a 1.1.0). Maybe I'm interpreting this wrong though.

If you already have some code prepared, a pull request would be helpful to continue the conversation.

@dlethin
Copy link
Author

dlethin commented May 15, 2015

Thanks for the reply -- I can work to getting a pull request ready for you. But in the short term, to explain:

In gitflow, work for the next release is done on the "develop" branch. When we are declared ready, we'll cut a release branch -- say "release/1.0.0". From the release branch we will build release candidates. So using gradle-git on the release branch, we want the normal version to be derived from the branch name - in this case 1.0.0 Each build off the release branch will increment the release candidate number.

While the release branch is being stablized, some team members can continue to do work on the develop branch for the next release -- that is why as soon as the release branch is cut and a build produced, we want builds off the "develop" branch to be interpreted as "1.1.0" (incrementing minor scope)

In my experience using gradle-git plugin, this doesn't work -- primarily because tags that were produced from the release branch are not ancestors of the develop branch anymore.

That being said, its quite possible I was doing something wrong. There's a lot to absorb walking through the code.

@ajoberstar
Copy link
Owner

That makes a lot more sense now. Thanks for the explanation.

I'm still thinking a max could be problematic in some situations. That said, there does need to be some global context that's understood about what other versions are in development in order to meet this type of use case. Could be based on just all tagged versions or maybe based on the branches tied to a version (or set of versions). I'll spend some more time thinking this through, but let me know if have more thoughts or questions.

@ajoberstar
Copy link
Owner

So I've thought through this some more and some form of the max version you described is probably the best way to support this in the current code.

One question. Is the max version determined from tagged versions or from branch names? Seems like branch names would be ideal, but tags would probably be easier to fit in to the code.

I think there may need to be a couple constraints to ensure maintenance use cases are still possible. Let me know if you think these would work out (or if they're overkill):

  • Current HEAD must not be an ancestor of the MaxVersion.
    • Basically just to ignore versions that are too far ahead in the graph. This should preserve maintenance.
  • Nearest normal version must be an ancestor of the MaxVersion.
    • Just to make sure they're working from the same base. Not sure this is necessary though.

I think this would at least guarantee some "nearness" for the MaxVersion, while still leaving it open enough for your use case.

If it's just based on tags, there wouldn't need to be any configurability besides maybe a flag that enables searching for nearby maxes. This could then just be implemented as part of NearestVersionLocator such that a nearby max would override the nearest any. The remainder of the code (Strategies, etc) wouldn't even need to know anything about the concept of the max.

@dlethin
Copy link
Author

dlethin commented May 22, 2015

Sorry for the delayed response and appreciate noodling on this more. I don't seem to be getting email notifications on this thread and have been focused on other things.

My initial feedback is in alignment with you. I think deriving max version from branch names would be ideal (in addition to tags), but yes, that seems to be harder to do in code as it exists today.

As an example of that scenario, consider cutting a branch release/1.0.0 off of develop branch

We might have two CI builds jobs that in parallel building these projects off these branches but perhaps still only producing -SNAPSHOT artifacts not not creating tags (until someone chooses to do a build with -Prelease.stage=rc

Without tags produced from the the release/1.0.0 branch, -SNAPSHOT versions off these branches would produce the same version # if version wasn't derived from branch name.

I was going down the path of trying to create a "gitflow" plugin similar to you "opinion" plugin which would make use of maxVersion, but punted on trying to support deriving from branch names. There was so much to consider -- how to register the branch names/patterns one would use.

As far as the constraints you propose, I think I have to noodle on them a bit more before I can comment. My brain is fried at the end of this week and I can't think of any counter-arguments that might negate their need.

I agree on your solution if it just tag based -- I hadn't thought about a flag to alert the behavior of NearestVersionLocator. That seems easier than my solution of creating a parallel MaxVersionLocator.

It is still my intent to share what I've done in a pull request, but work has been busy and its been hard to get back to this pet project.

Thanks again for your willingness and interest to accept this use case and Happy Memorial Day weekend.

Doug

@ajoberstar
Copy link
Owner

Happy Memorial Day to you too! Feel free to take your time. My goal is to address the branch and tag use case in semver-vcs (not sure how yet), but I'd still like to support something in the current API. Just let me know when you have something ready or something else to discuss.

@ajoberstar
Copy link
Owner

I'll continue this discussion on your PR (#117).

ajoberstar added a commit that referenced this issue May 30, 2015
In order to support #115, sort versions by ancestry
instead of distance, with version precedence being
the tie breaker. A flag is now available on
the locate() method to include tags from parallel
histories. These tags will always be treated as
normal versions, indicating that they are
developing towards that version and that the current
branch should not use it.
@ajoberstar ajoberstar added this to the wishlist milestone Apr 12, 2016
@dlethin
Copy link
Author

dlethin commented Nov 27, 2016

Sorry to never get a chance to followup on this issue. It was something I really wanted to personally get implemented, but never a priority for my team (Primarily a maven shop....) and I just could not find the cycles to concentrate on this before moving off that project a year ago and eventually leaving the company.

This past month I started working at a new company and there is one gradle project for which we are working to revise the continuous delivery processes, including the possibility to automate versioning artifacts through git meta-data. That project currently has a similar branching model to git flow and would run into the same issues described above, though one of the possibilities might be to change the branching model moving forward if causes more challenges than it solves. We have a spike in our upcoming sprint to flesh out, document, and model our developer/ci/cd workflows. We can then evaluate whether this plugin would meet our needs our of the box or if customization related to the above might be needed and make a decision based on how much work would be involved. My new company is not a java-shop -- This is primarily for our android mobile app and its variants. If customizing the plugin becomes necessary but more that half-a-week to get implemented in a fork, then auto-versioning might be de-scoped as there are (as always...) a boat-load of other higher-priorities in the queue. Will update this conversation with our path when I know more.

Thanks again.

@ajoberstar
Copy link
Owner

No problem, I'd like to see this one solved too, but also have had other priorities. Most of the time this one will require is just thinking through how the branching model would relate to the versioning. I don't high hopes for it being a very quick fix, but who knows. Thanks for the continued interest!

@ajoberstar ajoberstar changed the title Add support for a MaxVersionLocator? Support concurrent development in auto versioning Nov 29, 2016
@fvgh
Copy link

fvgh commented Jun 4, 2017

Hi @ajoberstar. I really like your plugin since it is easy to configure and quite flexible. So I only had a quick look at the semver-vcs, but think that it would be easier and cleaner to achieve my goals with this plugin.
I am using an adapted version of the Vincent Driessen's branching model (the basic of the git-flow) and different versions for sub-projects.

Because I am using this plugin, I can get rid of the release branches. As Vincent stated, there are two major reasons for a release branch:

  • Storage of version information from meta files which are part of the version control (thanks to your plugin, they are now just part of generated code)
  • Parallel work on multiple releases (I use continuous delivery, so I have no need)

So for me a simple custom startSearchAt (see draft proposal) is sufficient. For the described simplification of the Vincent Driessen's approach, the following customization is sufficient:

			tagStrategy.startSearchAt = { Branch currentBranch ->
				String startAtBranch = 'HEAD'
				if (currentBranch.getName().equals('develop')
				|| 	currentBranch.getName().startsWith('feature/')) {
					startAtBranch = 'master'
				}
				startAtBranch
			}

I know that this is only a starting point, but I think in the end, there will always be different needs of customization. So the git-flow approach should be a default configuration set than a hard implemented design as provided in #118. I think, if the customization is furthermore enhanced by providing the TagStrategy with a member to grgit from ReleasePluginExtension, the user can already configure nearly everything he wants in terms of git-flow in combination with a custom VersionStrategy:

  1. Check that hotfix branches of from master
  2. Find a way to put the release version information into the release branch name
  3. ...

Let me know whether my story sounds reasonable to you or whether I missed vital points. Maybe we can agree on a way forward, which in the end can lead to a git-flow support, but also covers simplified scenarios like I described.

@ajoberstar
Copy link
Owner

Hi @fvgh. Glad that others are interested in improving this, though I don't plan to accept any more changes to the gradle-git version inference. While my work has been sporadic, reckon (renamed semver-vcs) is going to replace the features here.

When you looked at semver-vcs/reckon, did you review the latest code? The documentation is very out of date, so if that's what you started with it may have misled you on the current features. I'd be interested to know if the new algorithm would meet your needs.

@fvgh
Copy link

fvgh commented Jun 5, 2017

I am afraid reckon has the same problem.

I can fully understand that you don't want to maintain both versions. Maybe you could state that in the ReadMe of this project?

The solution for reckon I can think of is based on the same principals as for gradle-git:

  • Make GitInventorySupplier customize-able by allowing to customize the commit used for starting the search/walk
  • Granting custom functions to access grgit (in case they want to do something really fancy)
  • Providing (or only describing in doc) how custom functions shall use exceptions (for example if the branch does not fit into naming schema)

I think the major question, is whether you agree to my story line provided in my previous comment.

@ajoberstar
Copy link
Owner

I think I understand the change you propose making, but I don't think I understand what problem it solves. Could you describe your use case in more detail?

@fvgh
Copy link

fvgh commented Jun 5, 2017

In my opinion your current implementation does not support custom branching models at all, or have I missed something?

I personally need the following custom behavior:

  • Versions are tagged on master branch only
  • Branch develop, feature/* and bugfix/* should always have a preliminary version based on the latest master version
  • hotfix/* should always have a preliminary version based on the master tag it is branching off
  • master branch should always have a normal version based on the nearest tag, or a preliminary version based on the nearest tag if dirty

So all this can be achieved by allowing custom starting point for starting the search/walk by GitInventorySupplier.

But then I still have no possibility to check miss-usage. What if the branch name used does not fit into the conventions described above? What do I do if a hotfix branch is not branching off from master? How do I check and tell the user?

So there the grgit access and an agreement how custom functions can report exceptions is inevitable.

@ajoberstar
Copy link
Owner

TL;DR Supporting this level of configuration is out of scope for both gradle-git and reckon. I plan to provide better guidance in the README on whether reckon is right for a particular project/person/team.

gradle-git was very customizable, far more than it should have been IMO. This has been a little bit of a maintenance problem for me and also just distracts from the core value: an opinionated and hands-off approach to versioning.

In reckon, I'm starting from an immutable and opinionated core and leaving the configuration to the edges (mostly for the user input type stuff, e.g. scope/stage).

The downside is that while reckon can support a number of branching models, it intentionally does not support them all.

From my understanding, the majority of git flow should comply with reckon's assumptions. Your described (and I think the pure) implementation of the master branch is where the conflict comes in.

One of reckon's core assumptions is that HEAD is the relevant starting point, and that its history has tags that inform what the next version should be. If master holds all of the tags, but those tags are on merge commits not visible outside of master, this is violated.

I do see that you could make it work with the configurability of the search start point, but this opens up too many possibilities for extension points within the core of reckon that I don't really want to pursue.

I certainly don't want to push someone away from a branching model that they like, but if you do want to use reckon/gradle-git, you would want to reconsider your approach to master. As long as you can keep the version tag being in the history of whatever development HEAD you are on, you should be fine.

@fvgh
Copy link

fvgh commented Jun 6, 2017

OK, thanks for the clarification. When I saw this issue, I thought there is some common interest.

@ajoberstar
Copy link
Owner

Closing this as the plan is to address it in reckon.

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

No branches or pull requests

3 participants