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

Auto-detect the value of the no-squash option #118

Merged
merged 1 commit into from
Apr 8, 2024

Conversation

earl-warren
Copy link
Contributor

@earl-warren earl-warren commented Apr 2, 2024

The auto-no-squash option is added to:

  • backport all the commits when the pull/merge request has been merged
  • backport the squashed commit otherwise

It is equivalent to dynamically adjust the value of the no-squash
option, depending on the context.

The no-squash option is kept for backward compatibility for a single
use case: backporting the merged commit instead of backporting the
commits of the pull/merge request request.

Detecting if a pull/merge request was squashed or not depends on the
underlying forge:

  • Forgejo / GitHub: use the API to count the number of parents
  • GitLab: if the squash_commit_sha is set, the merge request was
    squashed

If the pull/merge request is open, always backport all the commits it
contains.

Fixes: #113

Copy link
Contributor

github-actions bot commented Apr 2, 2024

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements
91.3% (+0.46% 🔼)
472/517
🟢 Branches
86.19% (+0.34% 🔼)
181/210
🟢 Functions
88.43% (+0.1% 🔼)
107/121
🟢 Lines
91.24% (+0.46% 🔼)
458/502
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢
... / gitlab-client.ts
93.98% (+0.23% 🔼)
81.82% (-1.52% 🔻)
91.67%
93.67% (+0.25% 🔼)

Test suite run success

177 tests passing in 16 suites.

Report generated by 🧪jest coverage report action from f591021

@lampajr lampajr marked this pull request as draft April 2, 2024 09:32
Copy link
Member

@lampajr lampajr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I absolutely agree on the overall approach, but I think that it relies on whether we can say if the merge_commit_sha (retrieved by this.getSha(pr);) is a merge commit or not (as it will always be filled in, either it is a merge commit, a squash commit or a rebase and merge commit).

Right now I am missing how this can be inferred from the pull request or github api but I might be missing somenthing too 😄

src/service/git/github/github-client.ts Outdated Show resolved Hide resolved
@earl-warren
Copy link
Contributor Author

earl-warren commented Apr 2, 2024

The original approach was not good because at the time the PR is examined the clone is not available and it is not possible to run git show to know more about it.

Here is another approach:

  • the list of commits is always extracted from the PR
  • the sha (from the head or the merge_commit_sha) is kept around to help decide later on

After the clone is done, the following pseudo logic chooses the commits that should be cherry-picked:

  async getPrCommits(configs: Configs, originalPR: GitPullRequest, git: Git): Promise<string[]> {
    let squash = configs.squash
    if (originalPR.state == "open") {
      squash = false
    } else if (squash === undefined) {
      squash = !git.isMerge(originalPR.sha)
    }
    if squash {
      return [originalPR.sha]
    } else {
      return [originalPR.commits]
    }
  }

Let me know if it is a sound approach and I'll keep going.

P.S. I think it never makes sense to have squash = true when the PR is still open but maybe there is a valid use case I did not think of.

@earl-warren
Copy link
Contributor Author

Now that I think about it... another way would simply be to clone before inspecting the PR so the existing logic can git show because the clone is already present. Probably simpler?

@earl-warren
Copy link
Contributor Author

Or not... clone would require the config to be available but that same config is the one parsing the PR so there is a chicken & egg problem.

@earl-warren
Copy link
Contributor Author

earl-warren commented Apr 2, 2024

Both have the parents.

$ curl -sS https://code.forgejo.org/api/v1/repos/forgejo/runner/git/commits/eb89a98c6a401bc0afc6f9a897a82de0dfcb9d8d | jq .parents
[
  {
    "url": "https://code.forgejo.org/api/v1/repos/forgejo/runner/git/commits/781606388cb46645bb84a31b48e081a4d350baea",
    "sha": "781606388cb46645bb84a31b48e081a4d350baea",
    "created": "0001-01-01T00:00:00Z"
  },
  {
    "url": "https://code.forgejo.org/api/v1/repos/forgejo/runner/git/commits/4f4ec159f0256b1d393cca5fae6732754a0a181b",
    "sha": "4f4ec159f0256b1d393cca5fae6732754a0a181b",
    "created": "0001-01-01T00:00:00Z"
  }
]

Looks like the better option, at he expense of an additional request. But... since it will git clone an entire repository shortly after wards it seems futile to save this. Or am I being careless?

@earl-warren
Copy link
Contributor Author

Here is a draft using the API endpoints to determine if the commit is a merge or not. It feels right this time.

@earl-warren earl-warren force-pushed the wip-squash branch 2 times, most recently from 585d522 to b2554a6 Compare April 3, 2024 05:07
@earl-warren
Copy link
Contributor Author

It was semi-manually tested at https://code.forgejo.org/earl-warren/racepr with something like

git fetch origin ; git reset --hard origin/main ; branch=b$(date +%s) ; date > adsf ; git add adsf ; git commit -m 'update one' ; sleep 1 ; date > adsf ; git add adsf ; git commit -m 'update two' ; git push origin HEAD:$branch ; pr=$(forgejo-curl.sh api_json --data '{"title":"t1","head":"'$branch'","base":"main"}' https://code.forgejo.org/api/v1/repos/earl-warren/racepr/pulls | jq -r .number) && forgejo-curl.sh api_json  --data '{"Do": "merge"}' https://code.forgejo.org/api/v1/repos/earl-warren/racepr/pulls/$pr/merge

and

git fetch origin ; git reset --hard origin/main ; branch=b$(date +%s) ; date > adsf ; git add adsf ; git commit -m 'update one' ; sleep 1 ; date > adsf ; git add adsf ; git commit -m 'update two' ; git push origin HEAD:$branch ; pr=$(forgejo-curl.sh api_json --data '{"title":"t1","head":"'$branch'","base":"main"}' https://code.forgejo.org/api/v1/repos/earl-warren/racepr/pulls | jq -r .number) && forgejo-curl.sh api_json  --data '{"Do": "squash"}' https://code.forgejo.org/api/v1/repos/earl-warren/racepr/pulls/$pr/merge

and the workflow

on:
  pull_request_target:
    types:
      - closed
      - labeled

jobs:
  backporting:
    runs-on: docker
    container:
      image: 'docker.io/node:20-bookworm'
    steps:
      - uses: https://github.com/earl-warren/git-backporting@b2554a678d5ea2814f0df3abad2ac4fcdee2d81f
        with:
          target-branch: other
          cherry-pick-options: -x
          git-client: codeberg
          auth: ${{ secrets.BACKPORT_TOKEN }}
          pull-request: ${{ github.event.pull_request.url }}

that does not mean it is the right way to do it but it is not complete nonsense either 😄

@lampajr
Copy link
Member

lampajr commented Apr 3, 2024

Hey @earl-warren , that is a great investigation and I was not aware of that way to determine whether a commit is a merge-commit or not, thus it looks a valid option to evaluate.

at he expense of an additional request. But... since it will git clone an entire repository shortly after wards it seems futile to save this. Or am I being careless?

No that's not a problem at all, I mean we don't have tight time restrictions here.

It was semi-manually tested at https://code.forgejo.org/earl-warren/racepr with something like

That's already a good early check, I will try to get some time today to review this approach as well!
Moreover I'll try to check whether we can use a similar approach for Gitlab as I'd like to keep all git platform feature-equivalent (but, in that case, we can do that in another pr)

@earl-warren
Copy link
Contributor Author

I'll complete this PR today and include GitLab as well. This is going to be good.

@lampajr
Copy link
Member

lampajr commented Apr 4, 2024

Hi @earl-warren, I had chance to take a look at this PR (at least from the generic workflow point of view) and I think it could work. I did not try yet but based on the changes you did I think the idea could work as long as this https://github.com/kiegroup/git-backporting/pull/118/files#diff-6e3804f9a1581e5155295a17bbbc71343fa375a15bf5383dd4c4049a44be09cdR54-R60 is enough/true.

There a couple of enhancement that could be done, e.g., now we could populate the commits variable directly from the the getPullRequest method since we already now the commits that need to be backported, but this is something we can do step by step.

@earl-warren
Copy link
Contributor Author

I'll complete this PR today and include GitLab as well. This is going to be good.

I'll work on this PR today.

Side note: this commit is in production at https://codeberg.org/forgejo/forgejo/pulls a behaves as it should.

@lampajr
Copy link
Member

lampajr commented Apr 4, 2024

Side note: this commit is in production at https://codeberg.org/forgejo/forgejo/pulls a behaves as it should.

That's great!!!

@earl-warren earl-warren force-pushed the wip-squash branch 2 times, most recently from 7510e7d to d74b7e3 Compare April 5, 2024 18:38
@earl-warren
Copy link
Contributor Author

I think this is a sane implementation. I thought it would be possible to just remove the no-squash option and rely on auto-detection for all cases. But there is one case that would create a non-backward compatible behavior (cherry-picking the merge commit). I also tried to change the no-squash option to have three state (undefined, true, false) instead of two. Adding another option is not elegant but it is easier to understand and makes for a clear implementation.

The GitLab support is made easy by the fact that the merge request has information regarding the status of the merge.

The PR is ready for review but I will leave it as a WIP: because it still lacks coverage for the logic. It would be great if you could review it before I polish it, in case there are changes needed that would require modifying the tests.

I really enjoy working on this action with you.

@lampajr
Copy link
Member

lampajr commented Apr 6, 2024

I think this is a sane implementation. I thought it would be possible to just remove the no-squash option and rely on auto-detection for all cases. But there is one case that would create a non-backward compatible behavior (cherry-picking the merge commit). I also tried to change the no-squash option to have three state (undefined, true, false) instead of two. Adding another option is not elegant but it is easier to understand and makes for a clear implementation.

Thanks a lot @earl-warren ❤️

The GitLab support is made easy by the fact that the merge request has information regarding the status of the merge.

That's really good to hear 😄

The PR is ready for review but I will leave it as a WIP: because it still lacks coverage for the logic. It would be great if you could review it before I polish it, in case there are changes needed that would require modifying the tests.

Yeah I agree with you, I think I can find some time today to review this - it should not take much time as we were already on the same page.

I really enjoy working on this action with you.

+100, I am really glad there are people like you who are very proactive on contributing back, great example of community! Moreover I hope that this action will really save some of your day2day work time 🤞

@earl-warren
Copy link
Contributor Author

It is already busy at work saving valuable time every day. ✨

Copy link
Member

@lampajr lampajr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so if I correctly understood the logic, that can be summarized as follow (correct me if I am wrong):

noSquash autoNoSquash SQUASH Behavior
false false true Try to cherry-pick single commit, merge_commit_sha
true false false Try to cherry-pick all commits in the original pull request
false true undefined *
true true undefined *

Am I right?

If I am right I think the overall implementation looks really great! I am trying to see if we can simplify the logic but nothing came to my mind, so I think we can proceed with this approach 🚀

I left a couple of comments.

PS: worth to add some table like this in the doc to help users understand how to configure the tool to enable a specific behavior and to highlight the default.

src/service/git/github/github-client.ts Outdated Show resolved Hide resolved
src/service/git/github/github-client.ts Outdated Show resolved Hide resolved
src/service/git/gitlab/gitlab-client.ts Outdated Show resolved Hide resolved
@earl-warren
Copy link
Contributor Author

Ok so if I correctly understood the logic, that can be summarized as follow (correct me if I am wrong):
noSquash autoNoSquash SQUASH Behavior
false false true Try to cherry-pick single commit, merge_commit_sha
true false false Try to cherry-pick all commits in the original pull request
false true undefined *
true true undefined *

* When `autoNoSquash` is undefined/false the behavior is the one already existing.

* Whereas when the `autoNoSquash` is set to true (*) the value of `noSquash` is _ignored_ and the value of `squash` is auto computed by the new logic https://github.com/kiegroup/git-backporting/pull/118/files#diff-6e3804f9a1581e5155295a17bbbc71343fa375a15bf5383dd4c4049a44be09cdR48-R66

Am I right?

Yes.

If I am right I think the overall implementation looks really great! I am trying to see if we can simplify the logic but nothing came to my mind, so I think we can proceed with this approach 🚀

If it was not for the case where you want to cherry-pick a merge commit, I think both no-squash and auto-no-squash could be removed. And the default behavior would be to cherry-pick a single commit when there is a squash or all commits if not.

If that behavior is really desirable, there could be a cherry-pick-merge-commit option.

But removing no-squash and adding cherry-pick-merge-commit or something would be a breaking change and you probably do not want that.

I left a couple of comments.

PS: worth to add some table like this in the doc to help users understand how to configure the tool to enable a specific behavior and to highlight the default.

👍

@earl-warren
Copy link
Contributor Author

earl-warren commented Apr 8, 2024

PS: worth to add some table like this in the doc to help users understand how to configure the tool to enable a specific behavior and to highlight the default.

A table was added in the README

@earl-warren
Copy link
Contributor Author

@lampajr does this look like a good enough test to complete this PR? The logic is simple right now. But it is pivotal to how GitLab/Forgejo/GitHub decide to squash or not and it feels right to keep it DRY, just in case it becomes more complex in the future and really requires good unit testing.

@earl-warren
Copy link
Contributor Author

earl-warren commented Apr 8, 2024

Manual tests.

auto-no-squash: true

      - uses: https://github.com/earl-warren/git-backporting@21d3fe29b9cb1b1c1a0ab0625dcb1b13f407eddc
        with:
          target-branch-pattern: "^backport/(?<target>(o.*))$"
          cherry-pick-options: -x
          git-client: codeberg
          auth: ${{ secrets.BACKPORT_TOKEN }}
          pull-request: ${{ github.event.pull_request.url }}
          auto-no-squash: true

merged commit

https://code.forgejo.org/earl-warren/racepr/actions/runs/26#jobstep-1-4

[DEBUG] Fetching pull request earl-warren/racepr/38
[DEBUG] cherry-pick the merged commit(s)
[DEBUG] Extracting branches from [backport/other] using ^backport/(?<target>(o.*))$

squashed commit

https://code.forgejo.org/earl-warren/racepr/actions/runs/27#jobstep-1-4

[DEBUG] Fetching pull request earl-warren/racepr/40
[DEBUG] cherry-pick the squashed commit 9b2eeb57e74115ee5d0456d983ce8b15ea709042
[DEBUG] Extracting branches from [backport/other] using ^backport/(?<target>(o.*))$

auto-no-squash: unset & no-squash: unset

      - uses: https://github.com/earl-warren/git-backporting@21d3fe29b9cb1b1c1a0ab0625dcb1b13f407eddc
        with:
          target-branch-pattern: "^backport/(?<target>(o.*))$"
          cherry-pick-options: -x
          git-client: codeberg
          auth: ${{ secrets.BACKPORT_TOKEN }}
          pull-request: ${{ github.event.pull_request.url }}

merge commit

https://code.forgejo.org/earl-warren/racepr/actions/runs/28#jobstep-1-10

[INFO ] [other] Creating branch bp-other-c6cc40c
[DEBUG] [other] Cherry picking commits..
[INFO ] [other] Cherry picking c6cc40ca7df7e1efd23b8c6064b674d3c17a872a
[DEBUG] [other] Cherry picking command git cherry-pick,-m,1,--strategy=recursive,--strategy-option=theirs,-x,c6cc40ca7df7e1efd23b8c6064b674d3c17a872a
[INFO ] [other] Pushing bp-other-c6cc40c to origin

squash commit

https://code.forgejo.org/earl-warren/racepr/actions/runs/29#jobstep-1-10

[INFO ] [other] Creating branch bp-other-1d1a9c8
[DEBUG] [other] Cherry picking commits..
[INFO ] [other] Cherry picking 1d1a9c8e5f0add54c4f9936a498e6b874aa88b7a
[DEBUG] [other] Cherry picking command git cherry-pick,-m,1,--strategy=recursive,--strategy-option=theirs,-x,1d1a9c8e5f0add54c4f9936a498e6b874aa88b7a
[INFO ] [other] Pushing bp-other-1d1a9c8 to origin

auto-no-squash: unset & no-squash: true

      - uses: https://github.com/earl-warren/git-backporting@21d3fe29b9cb1b1c1a0ab0625dcb1b13f407eddc
        with:
          target-branch-pattern: "^backport/(?<target>(o.*))$"
          cherry-pick-options: -x
          git-client: codeberg
          auth: ${{ secrets.BACKPORT_TOKEN }}
          pull-request: ${{ github.event.pull_request.url }}
          no-squash: true

https://code.forgejo.org/earl-warren/racepr/actions/runs/30#jobstep-1-8

[DEBUG] [other] Creating local branch..
[INFO ] [other] Creating branch bp-other-51b5105-e9a0bb1
[DEBUG] [other] Cherry picking commits..
[INFO ] [other] Cherry picking e9a0bb1fe756356fb48bf82d4c7718cd12bd71f5
[DEBUG] [other] Cherry picking command git cherry-pick,-m,1,--strategy=recursive,--strategy-option=theirs,-x,e9a0bb1fe756356fb48bf82d4c7718cd12bd71f5
[INFO ] [other] Cherry picking 51b5105cd3ca0f8da1364d24a88556ab8473dd27
[DEBUG] [other] Cherry picking command git cherry-pick,-m,1,--strategy=recursive,--strategy-option=theirs,-x,51b5105cd3ca0f8da1364d24a88556ab8473dd27
[INFO ] [other] Pushing bp-other-51b5105-e9a0bb1 to origin

@earl-warren earl-warren changed the title WIP: Auto-detect the value of the no-squash option Auto-detect the value of the no-squash option Apr 8, 2024
@earl-warren earl-warren marked this pull request as ready for review April 8, 2024 15:01
@earl-warren earl-warren force-pushed the wip-squash branch 2 times, most recently from bcefd52 to 44184ee Compare April 8, 2024 15:09
Copy link
Member

@lampajr lampajr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @earl-warren I think this is awesome!!!!

@lampajr does this look like a good enough test to complete this PR?

yeah they are, absolutely! I think the most important part was to guarantee the previous behavior is still woring without changes then we can always add more tests in the future if needed.

Furthermore it looks like you already did a lot of manual tests which are really great 🚀

I left just a couple of minor comments, they are not blockers so up to you. I'll wait your confirmation before merging this but FMPOV it is great!

README.md Outdated Show resolved Hide resolved
src/service/git/gitlab/gitlab-client.ts Outdated Show resolved Hide resolved
The auto-no-squash option is added to:

* backport all the commits when the pull/merge request has been merged
* backport the squashed commit otherwise

It is equivalent to dynamically adjust the value of the no-squash
option, depending on the context.

The no-squash option is kept for backward compatibility for a single
use case: backporting the merged commit instead of backporting the
commits of the pull/merge request request.

Detecting if a pull/merge request was squashed or not depends on the
underlying forge:

* Forgejo / GitHub: use the API to count the number of parents
* GitLab: if the squash_commit_sha is set, the merge request was
  squashed

If the pull/merge request is open, always backport all the commits it
contains.

Fixes: kiegroup#113

Co-authored-by: Andrea Lamparelli <[email protected]>
@earl-warren
Copy link
Contributor Author

Changes applied & README updated as suggested.

@lampajr lampajr merged commit 6042bcc into kiegroup:main Apr 8, 2024
6 checks passed
@lampajr
Copy link
Member

lampajr commented Apr 8, 2024

Changes applied & README updated as suggested.

Merged, thanks a lot for your contribution again @earl-warren ❤️

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

Successfully merging this pull request may close these issues.

Auto-detect the value of the no-squash option
2 participants