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

feat(manager/gradle): rewrite and extend parser #18484

Closed
wants to merge 25 commits into from

Conversation

Churro
Copy link
Collaborator

@Churro Churro commented Oct 23, 2022

Changes

Tl;dr

  • New Gradle parser implementation based on @zharinov's great https://github.com/zharinov/good-enough-parser
  • Covers all existing functionality + new Gradle patterns like dependencySet, mapOf and property("foo")
  • Resolves at least 9 open issues related to the exiting implementation
  • This PR is not meant to be merged, it is thought as a baseline for further, chunked PRs

Where is the catch?

  • Matching template strings is currently tailored to existing tests but doesn't work with random combinations like "a${b}c${d}e${f}..."

  • With the new parser, handler functions can't work async, so apply from() can't call await parseGradle()

    • To still make it work, I went ahead using fs.getFileContentMap() to load all Gradle files upfront
    • Personally, I think this is viable as Gradle files are overall small. The alternative using fs.readFilesSync() seems less favorable to me but probably I'm overlooking something here and there is another approach I haven't thought about yet.
    • Positive thing: apply from() now plays nicely with ignorePaths

Next steps

  • I'd very much appreciate your review of the concept itself, e.g. with regards to strict types

  • I'd hope for @zharinov to accept feat: Add query support for string-values and symbols in string-trees zharinov/good-enough-parser#144 and publish the lib with this extension (Update: done)

  • I've tested it extensively but, of course, it's hard to rule out all corner cases. So instead of a Big Bang merge, I'd suggest to do it piece-wise in co-existence with the existing parser

    • For example:
      • First replace variable assignments with the new parser + add support for Groovy maps and Kotlin mapOf
      • Replace simple dependency matching (= no interpolation) with the new parser
      • Add resolution of implicit Gradle plugins and so on
    • For each change / addition PR there would be a separate review + test repo + list of issues that are resolved

Please don't hesitate to share your thoughts on this plan too. For now, I didn't remove the tokenizer and other things that are obsolete with this re-implementation.

What's new?

Further Fixes

Future Work

Can be adapted (= reusing existing patterns) to match object { ... } definitions in buildSrc/*.kt files, see
#13295, #9410, #12397, #5480

Context

Documentation (please check one with an [x])

  • I have updated the documentation, or
  • No documentation update is required

How I've tested my work (please tick one)

I have verified these changes via:

  • Code inspection only, or
  • Newly added/modified unit tests, or
  • No unit tests but ran on a real repository, or
  • Both unit tests + ran on a real repository

@Churro Churro requested review from zharinov, viceice and rarkins and removed request for zharinov and viceice October 23, 2022 13:16
@Churro Churro marked this pull request as draft October 23, 2022 13:17
@zharinov
Copy link
Collaborator

Would be complicated to merge it by small steps, seems like parsing mechanism swapping is all-or-nothing?

@Churro
Copy link
Collaborator Author

Churro commented Oct 24, 2022

@zharinov, thank you so much for merging the two PRs into https://github.com/zharinov/good-enough-parser 👍

My initial idea was to parse a Gradle file twice in parseGradle() and do parts with the new parser, others with the old one. Upon second thought, this won't work because variables can be re-assigned and using only the "last assignment" will incur wrong dep extractions.

Another approach could be the following:

  • First create a pure refactoring PR, similar to how you did with bazel, swap the parser and maintain feature parity.
  • Then do individual follow-up PRs that add support for further patterns like dependencySet and mapOf.

@zharinov
Copy link
Collaborator

I think, <drop-in replacement> + <further additions> is a good strategy

Copy link
Collaborator

@rarkins rarkins left a comment

Choose a reason for hiding this comment

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

I'm happy if @zharinov and @Churro are in agreement. Based on past experience I assume you don't mind that if we find regression errors, we'll hope to fix them rather than roll back?

@Churro
Copy link
Collaborator Author

Churro commented Nov 6, 2022

Sure, I'm ready to address potential regression errors.

Aligned with what @zharinov wrote above, the idea is to first replace the existing parser (#18663), followed by a cleanup PR to remove the old parser + subsequent PRs that add dependencySet, mapOf, support for implicit Gradle plugins, etc

@rarkins
Copy link
Collaborator

rarkins commented Nov 6, 2022

So will most of this PR be split off into a drop in refactor PR?

It would be good if we can avoid any new PRs even if they are correct. That way we don't need to worry about rollbacks causing auto closes

@Churro
Copy link
Collaborator Author

Churro commented Nov 6, 2022

So will most of this PR be split off into a drop in refactor PR?

That was the idea with PR #18663. First, to transition all existing functionality to the new parser and then to have subsequent PRs bringing in new features, link issues they resolve and provide evidence via test repos. In case a PR needs to be reverted that brought in e.g. mapOf functionality, it would auto-close only PRs for related deps.

It would be good if we can avoid any new PRs even if they are correct. That way we don't need to worry about rollbacks causing auto closes

Wouldn't this be the same situation if this fully-fledged PR here (or basically any PR that extracts more deps than before) was merged and needed to be rolled back? Test-wise the refactoring PR #18663 shows parity with the existing parser, so even if reverted, no auto close should happen as the current parser finds the very same set of deps.

Related to that, I assume that separate PRs would simplify code reviews with smaller changesets and more obvious reasoning why some changes were made. In any case, I'm ready to advance as you prefer.

P.S.: There are still 2-3 feature things that are not added yet here ATM (e.g. a solution for #8728) but where I thought subsequent PRs would be good enough.

Copy link
Member

@viceice viceice left a comment

Choose a reason for hiding this comment

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

so this has a behavior change and possibly closes PR's if some recursive applied files are not inside manager file list 🤔

lib/modules/manager/gradle/extract.ts Outdated Show resolved Hide resolved
@Churro
Copy link
Collaborator Author

Churro commented Nov 8, 2022

so this has a behavior change and possibly closes PR's if some recursive applied files are not inside manager file list 🤔

That's correct. However, from a user perspective, if you already redefine fileMatch to not include such files, I'd also expect renovate to not process them via apply from either. So, perhaps, this change can also be seen as a correction move towards more intuitive renovate behavior.

@rarkins
Copy link
Collaborator

rarkins commented Nov 17, 2022

Should this now be deconflicted off main and ready for review, or is there some development work remaining, features to be divided up further, etc?

…rewrite

� Conflicts:
�	lib/modules/manager/gradle/parser.spec.ts
�	lib/modules/manager/gradle/parser.ts
�	lib/modules/manager/gradle/parser/common.spec.ts
�	lib/modules/manager/gradle/parser/common.ts
�	lib/modules/manager/gradle/parser/handlers.ts
@Churro
Copy link
Collaborator Author

Churro commented Nov 17, 2022

@rarkins, my plan would be as follows:

  • Harden the state of the current impl with further tests: test(manager/gradle): add further tests #18977
  • Add further PRs that address individual feature additions, e.g. dependencySet, mapOf, implicit gradle plugins etc. + demo repos and reference of issues that are resolved by them - basically as I'd do for other PRs as well.

If that's fine for you too, we could close this PR right now. If you'd prefer just one PR (at least for the changes pushed here so far), I'd be fine too and would then simply restructure this PR.

I'm working on further augmenting the parser implementation and currently have the following things on the radar (going into separate PRs I'd say):

…rewrite

� Conflicts:
�	lib/modules/manager/gradle/parser.spec.ts
@Churro Churro closed this Nov 27, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants