1

At my workplace, we use a monthly release branch that's shared across several developers.

Gradle version is 2.14.1

We manually trigger the code build and release (task) using Jenkins (which is effective running - gradle clean compileJava release)

The whole process takes about 30-40 minutes, basically compiling, generating the artifacts, running Junit tests and uploading the artifacts to the Artifactory.

Eventually it comes to the step of tagging and pushing the version number: preTagCommit, which tries to update the gradle.properties and bumps up the version number to it and commits and pushes.

At this point, if there have been no commits on the branch for the last 30-40 minutes (Since the build was manually triggered), the release works successfully.

The moment there is even a single commit between the whole process it fails with error: Execution failed for task ':preTagCommit'.

*
error: failed to push some refs to 'xxx.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
*

I tried several hacks and searched the documentation but not leading to any proper solution yet.

This is how my release step looks like:

***
    release {
        project.setProperty("gradle.release.useAutomaticVersion", "true");
        newVersionCommitMessage = "Re-snapshoted project to version "
        preTagCommitMessage = "Preparing version for release "
        tagCommitMessage = "Tagging Release "
        tagPrefix = "calypso"
        requireBranch = ""
        // Sometimes the plugin trips over its own feet with modifying the gradle.properties
        // then complaining it has changed.
        failOnCommitNeeded = false
        pushToCurrentBranch = true
    }
***

Apologies if this has been asked previously, all the solution I found was general approach to git rather than from someone using Gradle-Release-Plugin with git.

Any response will be greatly appreciated.

  • Still rocking that outdated gradle I see. What version is your gradle releases plugin? – smac89 May 06 '20 at 03:31
  • Also what do you want to happen in the case that there is a commit that hasn't been merged? – smac89 May 06 '20 at 03:42
  • It looks like you are not sure about the merging strategy you want to use on that special case. Just pulling and merging that changes would sneak changes in your releases you aren't aware of. Did you consider using special release branches? – Clijsters May 06 '20 at 09:31
  • @smac89 My gradle release plugin was 2.0.2, now I upgraded it to 2.6.0. But still it behaves the same. What I am looking for is, if there is a commit that hasn't been merged (that comes in during the build process). I don't want those changes to be included onto the current tag. I am happy to wait until the next build. – Vivek Narayanan May 06 '20 at 21:52
  • @Clijsters, The behavior I am looking for is, even if there are new check-ins after I start the build process - I don't want them to be included in the build and also not fail the build in process. I would ideally like for the new items to be considered only on the next build. Thanks for suggesting the special release branches, would you be able to give some examples? Are you referring to something like this: https://nvie.com/posts/a-successful-git-branching-model/ – Vivek Narayanan May 07 '20 at 00:02
  • Yes, exactly. We use release-branches like on the branching page you linked. – Clijsters May 07 '20 at 10:27

2 Answers2

1

I believe the flag you're looking for is mentioned in the GitHub page of the plugin:

Eg. To ignore upstream changes, change 'failOnUpdateNeeded' to false:

release {
  failOnUpdateNeeded = false
}

Edit

It seems that the above flag does not work for some reason. Yet, there is another way to accomplish this, and that is to add a force push option to the git option of the release extension (what a mouthful to say).

release {
  git {
    pushOptions = ['--force']
  }
}

This will basically force-overwrite the branch (see problem analysis below) unless the server on which the repository is hosted, is tuned to reject such forced pushes. In that case, there is really no option you can pass here that will help.


Problem analysis

As I said in my last comment below, the part that fails is when the extension runs the preTagCommit task, and then tries to push this commit to the upstream branch (master for example). You can see this push command right here.

Well of course this will fail if someone else already pushed a commit to that branch.

Now an easier way to fix this would have been if the plugin authors gave us the ability to say

Don't push preTag commits to upstream

or

Push preTagCommits to a branch named foo

Unfortunately they haven't given us this option (atleast from what I've gathered so far). So I have come up with some hacky solutions to circumvent this.

Solutions/Hacks

Specify pushToBranchPrefix Create a tagged branch

This is another option that can be passed to the git object which causes all pushes it does to be done to a specific branch with a given prefix rather than the current branch:

For example:

afterReleaseBuild {
  doFirst {
    project.exec {
        executable = 'git'
        args 'checkout', '-b', 'v1.x.x@master'
    }
  }
}

Replace v1.x.x with something that matches the tag for the current release.

What this does is that whenever the plugin attempts to push/commit, it will instead push/commit to this branch we created, and finally create the tag from this branch also.

I think this is probably the best option

Disable the preTagCommit task

Since the preTagCommit task is where it fails, we can disable the task, and it won't be run anymore:

tasks.named('preTagCommit') {
    enabled = false
}

Determine for yourself if this is acceptable.

Dynamic push behaviour

Because I've seen the source code, I can see that the reason it pushes is it sees that git.pushToRemote is not null. Knowing this, we can dynamically control this behaviour.

tasks.named('preTagCommit') {
  def pushToRemote = null
  doFirst {
    project.extensions.configure(net.researchgate.release.ReleaseExtension) {
      pushToRemote = it.git.pushToRemote
      it.git.pushToRemote = null
    }
  }

  doLast {
    project.extensions.configure(net.researchgate.release.ReleaseExtension) {
      it.git.pushToRemote = pushToRemote
    }
  }
}

This way, in the preTagCommit, it won't push anything, but will succeed for everything else.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • Hi smac89, Thanks for your comments. I did try that option previously it didn't do the trick, but I agree it is quite promising. I also realized that I was using a much older gradle release plugin (2.0.2), though upgrading to 2.6.0 still didn't resolve the above issue. But It did reduce my build time by 50% (Thanks for that) ! – Vivek Narayanan May 06 '20 at 07:14
  • @VivekNarayanan Glad it reduced your build time. The purpose of the `failOnUpdateNeeded` flag is unclear to me. You may want to open an issue with the plugin authors to further explain what this flag does. I took a look at the plugin source and I think [this function](https://github.com/researchgate/gradle-release/blob/6540fa35f8d6a84f11314bb48a0c81ab44842a5c/src/main/groovy/net/researchgate/release/GitAdapter.groovy#L123) is where it fails because it tries to push the commit to the current branch. I have updated my answer above with some workarounds you can try – smac89 May 08 '20 at 01:37
  • Hi @smac89, I had go by deep digging around the groovy and found if I set pushToRemote = '', the release plugin stops pushing. After the gradle command I followed up with Pull first and then Push, which resolved the issue and gave the behavior expected.Glad to see you have shared the same hack here though in a different place. Thanks a lot! – Vivek Narayanan May 08 '20 at 05:26
0

The easiest way out for my situation was to simply modify for my release plugin component in the build.gradle:

***
release {
....
 pushToRemote = ''
....
}
***

This ensures the release plugin doesn't push!

After control comes from the gradle release, I followed up with:

Git pull origin

Git push --tags origin

This nicely synchronized my commits and also follows the pull before push rule!

Though this is a hacky solution, I was suggested by @Clijsters to use Special release branches to solve this issue.