#1143  Do not run pull request builds automatically when target branch is updated
Released
jbauer opened 1 year ago

I am not quite sure if this is a bug or just a missing configuration feature.

I have created a CI job that triggers on pull request opened/updated for target branch development:

  triggers:
  - !PullRequestUpdateTrigger
    branches: development

Also the project has no branch protection setup. Even if it would have a branch protection setup imagine the required builds setting is empty (no any). So no build is required in both cases.

Now when I have multiple pull requests opened for target branch development each has triggered its own build. That is fine. However if I now do a commit on development (either directly or by merging a pull request) all these builds are triggered again even though the pull requests itself have not been updated. This can easily result in a lot of builds if you have quite some pull requests in review and these builds can block the build/agent cluster for quite some time (and also block release jobs).

I can see that OneDev does this to make sure that once you hit the Merge button it is guaranteed that the build of development branch will work after merging. However branch protection setup does not require such a build. I think the above behavior should only occur if you have a branch protection setup created and the CI build is selected in required builds configuration property of the branch protection.

Github has something similar in branch protection setup. You can enforce status checks and one status check is to check the branch against the base branch. You can choose between strict (branch must be up-to-date with base branch which results in builds being triggered if base branch is updated), loose (you can merge a branch even if it is not up-to-date with the base branch with the risk of having a failed build after merging) and disabled (you can merge whenever you want).

Maybe there is a clever solution that reduces the amount of automatically generated builds by OneDev because it is costly to scale once pending pull requests are more than just a few. For example I could imagine that the merge button could trigger the build against the target branch as part of the merge process (if branch protection says a successful build is required). If the build fails, merging aborts and an automatic comment is created. In addition a check against <target branch> button could exist that only triggers the build but does not merge. Then you would not need to generate many builds when target branch is updated.

So in short:

  • CI build should run always when branch protection requires a successful build.
    • In this case an option would be nice to either generate builds automatically on branch changes or trigger the build as part of the merge button
    • If "trigger build as part of merge" is selected then a check against <target branch> button should appear in pull requests UI so that the check can be done manually at any time as well.
  • CI build should not run always when branch protection does not require a successful build or no branch protection exists
    • Instead buttons to manually check against target branch could be provided

What do you think? Have you ever used OneDev with many pull requests and a larger project/team? Currently this should result in lots of builds being generated.

Robin Shen commented 1 year ago

OneDev always runs builds against merge commit of target branch and source branch of a pull request, as it can signal integration issues as soon as possible. It runs even if it is not required by branch protection, as it is triggered by:

triggers:
  - !PullRequestUpdateTrigger
    branches: development

It is still useful even if branch protection is not enabled, as it can serve as a important reference when check/review the pull request.

It does generate massive builds for massive pull requests. But I think computation resource is cheap compared to project stability, and you can easily connect more agents to OneDev server to run more CI builds concurrently...

Robin Shen commented 1 year ago

Also if deployed in k8s environment, OneDev build farm can utilize k8s node pool auto-scalinng to scale up/down based on build load.

jbauer commented 1 year ago

The point is that it does not have to be a massive amount of pull requests to generate a massive amount of builds. It all depends on the number of commits on the target branch within the duration of a single build. This can quickly get out of control if you have a project with a slow build.

Imagine a project that takes 15 minutes to build and test. Now I have 15 minutes to commit to target branch and produce additional integration builds based on the pull request count. If I have 10 pull requests open (which I would not call massive) and do 5 commits on target branch within 15 minutes then 50 builds are scheduled. If one build requires 8 GB of RAM then you would need quite some hardware resources to manage that in parallel. 50 VMs running agents and a total of 400 GB of RAM. Would you buy that for 10 pull requests and 5 commits? Probably not. If you install OneDev in a public cloud you might get away with it by temporarily pay a bit more, but if you have to use a local cluster you have a fixed amount of resources available.

No matter what hardware resources you buy, because the build takes some time the build cluster can be saturated easily. Once it is saturated you can not do release builds and because the build takes some time the build cluster is saturated for quite some time. Of course I could split my e.g. 50 agents into two groups, one for CI builds and one group for release builds. Then I have reserved free resources for release builds at the cost of even longer CI builds. However these reserved resources are only used occasionally.

I think it is not really an issue for projects that build and test quickly but if you have slow building projects it can be a problem. So I think OneDev should give some options on a per project level to configure build behavior.

The easiest option would be to be able to configure OneDev to only record that changes on the target branch has been done instead of scheduling a new build right away. In that case OneDev would tell the running build to re-run itself after completion because the target branch has become dirty during the build. That would cost more time since builds are now sequential (per pull request) but it reduces parallel computing power needed in the build cluster. Given the above example instead of 50 builds you would only have 10 builds at any time since I have 10 pull requests. So I might be fine with just 20 agents instead of 50+.

Alternatively OneDev could be configured to only do integration builds when pull requests are opened/updated and ignore changes to the target branch. In that case OneDev would need to do a final integration build when the user hits merge. This final integration build would use the same pattern as above, that is, it would need to re-run itself if it detects changes to the target branch after the build has been started. So the merge action will take longer or eventually fail if the build fails.

Robin Shen commented 1 year ago

Thanks for explaining details of your scenario. Will add more options to control pull request build behavior.

jbauer commented 1 year ago

Thanks that would be great. Both alternatives above guarantee correctness at the cost of re-running builds in a serialized manner. Not sure if you like that.

But I think it would also be fine if OneDev had additional options similar to Github to give up some correctness and hand over the responsibility to the person who does the merge. For example if a project does not allow direct commits to the main branch then the person merging pull requests could re-run certain pull request integration builds manually after a different pull request has been merged because that person can know that these pull requests might affect each other. So the risk of merging something that is broken after merging can be minimized outside of OneDev. If a project allows pull requests and direct commits to the main branch then the situation is more complex for the user. But OneDev UI could help the user by displaying a message that new commits have been done to the pull request's target branch since the last integration build.

Robin Shen commented 1 year ago

For your first suggested approach, instead of re-run build when target branch is changed, it might be more reasonable to cancel currently running build and fire new build, as running build against outdated target branch does not provide too much value, and it will also save more resources. Currently CI builds on branches works this way, and I just need to apply this logic to pull request builds.

More flexible pull request build firing strategy can also be implemented, something like github's strict/loose mode. However running strict integration build at time of merging (and check back if build fails) might make the pull request workflow pretty complex. Pull request can just get merged if loose build succeeds, and in target branch the merged commit can also fire a branch build, and will notify committers when the integration build fails. This already serves the purpose pretty well I think.

Robin Shen commented 1 year ago

Opened issue #1150 to cancel outdated pull request build

jbauer commented 1 year ago

For your first suggested approach, instead of re-run build when target branch is changed, it might be more reasonable to cancel currently running build and fire new build, as running build against outdated target branch does not provide too much value, and it will also save more resources. Currently CI builds on branches works this way, and I just need to apply this logic to pull request builds.

Sure cancel builds would also work but I thought it might not be too easy since you do not know what exactly is executed during a build. Thinking about risking zombie processes if force killing the container / shell.

More flexible pull request build firing strategy can also be implemented, something like github's strict/loose mode. However running strict integration build at time of merging (and check back if build fails) might make the pull request workflow pretty complex. Pull request can just get merged if loose build succeeds, and in target branch the merged commit can also fire a branch build, and will notify committers when the integration build fails. This already serves the purpose pretty well I think.

Hm yeah I think it would be fine to get notified about problems after it has been merged. Only exception would be if you have continuous delivery and everything merged will be deployed into production automatically. But then you might not want to use loose policy or you manually start a build to be extra sure.

Jerome St-Louis commented 1 year ago

The pull request automatically rebuilding when target builds is a major annoyance for us that we absoutely need to disable. It multiplies the resource usage by a huge factor (translating into higher electricity/computing resources bills and wear & tear, noisy fans from local build computer and great loss of productivity from the noise) and quickly uses up disk space with artefacts (translating into future failed builds).

There needs to be an option to separately configure Pull Request triggers for "updated" vs. "opened". With this option, users could decide the workflow that suits them. When not auto-building on updates, we can simply re-run the build when rebasing the pull-request or prior to merge it.

Robin Shen commented 1 year ago

The upcoming 8.0 release will allow to run pull request integration build manually as necessary.

Robin Shen changed title 1 year ago
Previous Value Current Value
Builds for pull request opened/updated are re-run when target branch is updated
Do not run pull request builds automatically when target branch is updated
Robin Shen changed fields 1 year ago
Name Previous Value Current Value
Type
Discussion
Improvement
OneDev changed state to 'Closed' 1 year ago
Previous Value Current Value
Open
Closed
OneDev commented 1 year ago

State changed as code fixing the issue is committed

OneDev changed state to 'Released' 1 year ago
Previous Value Current Value
Closed
Released
OneDev commented 1 year ago

State changed as build #3440 is successful

Robin Shen commented 1 year ago

With 8.0 release, commits in target branch will no longer trigger pull request builds. The branch protection rule also sees a new option Require Strict Pull Request Builds, which will prevent pull request from being merged if builds are outdated, and user can simply run required jobs again from pull request UI to make it up-to-date in order to merge.

Jerome St-Louis commented 1 year ago

Thank you so much!

Referenced from commit 1 year ago
Jerome St-Louis commented 1 year ago

@robin It seems that two builds still get triggered simultaneously when pushing a commit to a branch that has an opened Pull Request. One gets submit reason: "Pull request #[number] is opened/updated", whereas the other gets submit reason "Branch '[branch name]' is updated".

I thought this is what was being fixed by addressing this issue?

Robin Shen commented 1 year ago

This issue addresses the problem that when target branch of a pull request is updated, the pull request build should not run.

In your case, I guess your source branch of the pull request is updated, that will trigger a pull request build against merged commit of source branch and target branch. Also another build will be fired to verify head commit on source branch itself as you have the trigger rule "when branch is updated" covering the source branch. They are for different purposes.

Jerome St-Louis commented 1 year ago

@robin But this is a very real problem that constantly takes 2x more computing resources / more time to build / more artifacts space because a pull request will often remain active and new commits will be pushed to the source branch. We really do not ever want a Pull Request to automatically trigger a build, since we already have an automatic build on pushing a commit to any branch.

Robin Shen commented 1 year ago

Then you can just remove the pull request trigger for relevant jobs.

Jerome St-Louis commented 1 year ago

@robin Thanks, I guess we can do that. But I think I was previously wishing for a separated open vs. update pull requests.

So that if you open a pull request against an existing branch that was pushed a while back it would automatically trigger a build.

But then I guess you could just manually trigger the build.

Robin Shen commented 1 year ago

Yes, manual build for a pull request is possible if you configured branch protection rule to require particular job to be successful.

In the mean time, pull request open/update trigger will be separated for easier control of desired builds in next release.

issue 1 of 1
Type
Improvement
Priority
Normal
Assignee
Issue Votes (1)
Watchers (5)
Reference
onedev/server#1143
Please wait...
Page is in error, reload to recover