caitp / grunt-bower-release

Push bower releases to different endpoints
17 stars 11 forks source link

Overwriting Existing Versions #35

Open mikeborozdin opened 10 years ago

mikeborozdin commented 10 years ago

Often, you might want to overwrite an existing version. For example, you might want to have a pre-release snapshot dependency (e.g. 1.0.0-SNAPSHOT). A snapshot version indicates that it might change over a course of time.

For example, two people - Alice and Bob - are working on a Sprint that will result in releasing version 1.0.0. While writing code though they put a version number of 1.0.0-SNAPSHOT. If Alice finishes her user story first, her 1.0.0-SNAPSHOT goes into a dependency repository. When Bob finishes his story later on, the snapshot version will be updated and will include both Alice's and Bob's code.

Now, the challenge with Bower is that it uses Git tags as versions. If you push a tag 1.0.0-SNAPSHOT, all subsequent pushes of the same tag will fail - and new code will not land in Git.

One way of doing solving the problem is to delete a git tag before pushing it. The problem with this approach though, if you do 'bower update' or 'bower install' it will not know about a newly released dependency, as it has the same tag name.

Fortunately, there is a solution - one can suffix tags with '+[some unique ID]'. That unique ID can be a commit SHA or a timestamp.

In this case, if you have a specific dependency version expression in your bower.json, e.g.

"dependencies": {
  "your-dependency": "1.0.0-SNAPSHOT"
}

It will easily pick a dependency whose Git tag is '1.0.0-SNAPSHOT+2014', see node-semver for a reference. However, there is a minor issue here. If you simply push a new Git tag (e.g. '1.0.0-SNAPSHOT+2014', '1.0.0-SNAPSHOT+2015'), every time you're invoking grunt-bower-release, Bower will not pick it.

However, there is still a workaround for that - grunt-bower-release can list all tags and delete the tags which correspond to the current version before pushing a new tag. In this case, 'bower update/install' will successfully pick an overwritten dependency without having to clean its cache.

I have verified that behaviour experimentally, however, I am still yet to submit a pull request with the described functionality.

caitp commented 10 years ago

so you want it to delete remote tags before pushing the tags, is that right? that seems very endpoint specific, but doable. I'm not sure if it is necessarily good to force that behaviour, though.

Should it be enabled by a flag?

mikeborozdin commented 10 years ago

Hi,

Yes, that's correct. My goal is to delete both local and remote tags before pushing new ones.

Speaking of it being specific, this is a Grunt plug-in for making releases of Bower dependencies. And Bower, or more specifically, it's semantic version resolution component works in such a way that if there's already a downloaded dependency whose tag was, for example, '1.0.0-SNAPSHOT-2013', then it would simply ignore a new tag that is '1.0.0-SNAPSHOT-2014'. As a result, one has to remove old tags, but only the ones which correspond to the same version, e.g. 1.0.0-SNAPSHOT.

In terms of enabling it by a flag, I wonder if it should work only for SNAPSHOT dependencies.

What do you think?

caitp commented 10 years ago

I don't really like the idea of specific tag patterns having an effect on the behaviour of the task, I'd prefer it was an explicit decision made by the caller

mikeborozdin commented 10 years ago

Caitlin,

Such behaviour is not enforced by a specific tag pattern, but by a version which comes from bower.json.

In other words, it is a version that influences a tag name. For instance, if a version in bower.json is 1.0.0 we will just attempt* to create a tag with the name of 1.0.0 and push it. Please, note I'm saying 'attempt', because if such a tag already exists, we will fail to push. In this case, it is desired behaviour, because we don't want to update a released version.

However, if bower.json happens to have a version of '1.0.0-SNAPSHOT' that we know that it's a snapshot version that can be updated. Hence, in order to perform such an update we need to create a tag that is suffixed with a unique string, for example, a commit SHA or a timestamp, and delete all existing tags related to '1.0.0-SNAPSHOT', so that 'bower update' could pick a new snapshot version.

caitp commented 10 years ago

well I agree with not overwriting stable versions (but who decides what is stable, a tag/codename doesn't really do that). It might be better to have the caller enforce that decision instead

mikeborozdin commented 10 years ago

Okay, do you reckon we should introduce a new flag in the options?

Say, snapshotVersion = true | false. If you find such a name too ambiguous, we can try 'overrideExistingVersion'.

Also, it will only work if we push a branch with it. Otherwise, next time you perform a release, 'git clone' will tell you that HEAD refers to a non-existent ref.

caitp commented 10 years ago

I think snapshotVersion is ambiguous, yeah. overrideVersion or replaceVersion sounds good.

If you think this is the more common case (which for some uses, it might be), then it might be better to make it the default behaviour (but this would be a breaking change)

mikeborozdin commented 10 years ago

I actually meant to say overwriteVersion, but replaceVersion is good, as well.

A breaking change? Well, if overwriteVersion is set to false by default, then it should not break existing builds.

The question is though shall we fail a release if overwriteVersion = false and branchName is not set? Or shall we fallback to master by default?

caitp commented 10 years ago

I don't think you ever actually need to set a branch name, so there's no reason to change that behaviour, afaik

mikeborozdin commented 10 years ago

Hmmm... interesting, I have just tried using a non-forked grunt-bower-release plug-in. And starting from the second release, I mean when you're calling 'grunt bowerInstall' at least twice, it starts telling

warning: remote HEAD refers to nonexistent ref, unable to checkout.
mikeborozdin commented 10 years ago

The reason for that is the absence of any branch...

caitp commented 10 years ago

it's because you end up with multiple HEADs, I think. So okay, I guess you do want a branch there.

mikeborozdin commented 10 years ago

I've submitted a new pull request https://github.com/caitp/grunt-bower-release/pull/40