-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Add individual precommit task plugins #56926
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
Conversation
Each precommit task is currently registered inside the shared PrecommitTasks class. Having a single class with all precommit tasks makes individualizing which precommit tasks are needed based on type of project difficult, where we often just disable somet tasks eg for all qa projects. This commit creates plugins for each precommit task, and moves the configuration of the task into each plugin. PrecommitTasks remains for now, and just delegates to each plugin, but will be removed in a future change.
Pinging @elastic/es-core-infra (:Core/Infra/Build) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great and definitely the right way to go something is dying in me every time I see a task.enabled = false
:)
I wonder if we should rethink the term precommit
here as we implies this is ONLY executed pre commit. But all those things should and do also run on CI. In other builds I often saw the term sanityCheck
. I'm not saying we need to fix this here as part of that PR but just food for thought / discussion.
/** | ||
* Base plugin for adding a precommit task. | ||
*/ | ||
public abstract class PrecommitPlugin implements Plugin<Project> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to not extend this Plugin in other PrecommitPlugins but use composition here. So we would have a PreCommit plugin that is applied by all other specific PreCommitPlugins (e.g. the CheckstylePreCommitPlugin). The PreCommitPlugin would only create a 'precommit' task that depends on all tasks of a certain marker
type like tasks.withType(PreCommitTask.class)
. The other specific precommit plugins would only need to ensure that the tasks they create are of type PreCommitTask which can be just common interface.
Also this maybeRegister
method is relative expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about it an alternative would be if the specific PreCommit plugins are just responsible to wire things against the precommit task. That way you could skip the specific common task type. so these plugins would look always like
...
apply(PreCommitPlugin)
// register tasks I want to wire
tasks.named("precommit").configure { dependsOn 'specificTask; }
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to not extend this Plugin in other PrecommitPlugins but use composition here
Using inheritance allows us ensure the correct wirings happen. With composition, we can forget to hook up the new task to precommit, but with inheritance we enforce this because the class and createTask methods are abstract. Additionally, it wouldn't make sense to have precommit
alone, it only makes sense in the context of having a concrete task like forbiddenapis.
The other specific precommit plugins would only need to ensure that the tasks they create are of type PreCommitTask
We can't guarantee this since some tasks are external, like forbidden apis or checkstyle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I would still split out the generation of the precommit task to a small (internal) plugin (e.g. PrecommitBase Plugin. By internal I mean we would not need to provide a public id for the plugin or even make it package protected. And here we would just apply this base plugin. This way we
a) ensure this task registration is done in a gradle idiomatic way
b) ensure its only applied by the specific PreCommit Plugins.
this is a common pattern in gradle where a plugin just provides a lifecycle task and other plugins hook into this lifecycle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a separate plugin that creates the precommit task might be the way to go as well. Primary reason being that maybeRegister
does exception-based flow control, which is incredibly expensive and basically negates all benefits of lazy tasks. In fact, I'd recommend we completely remove that method so folks don't use it. We are throwing hundreds of exceptions here during configuration time. Filling all those stack frames has a big penalty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 small general thought on findByName. findByName
often is an indicator of weak modelling that can be improved. I think in the build there's a lot of logic that is based on tasks that are around which should be more about what type of project (which plugins are applied) are we dealing with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findByName often is an indicator of weak modelling that can be improved
That's definitely true. If you have to check if a task exists that indicates a code smell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After chatting with Ryan I think I'm fine with plugin class extension. I'm less concerned about extending plugins when the superclass is abstract, that at least indicates this is purposeful. Extending non-abstract plugins is a no-no, and composition should be used instead as you say.
We can then have the abstract plugin apply a plugin which creates the precommit
task that way Gradle handles idempotency for us rather than using maybeRegister
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can then have the abstract plugin apply a plugin which creates the precommit task that way Gradle handles idempotency for us rather than using maybeRegister.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed b5dff57 which no longer uses maybeCreate. Instead we have an inner plugin that creates the task, but we still use inheritance to ensure we don't forget to add a task to it.
You are correct that precommit is a sanity check. The terminology comes from the Lucene build (we have quite a few Lucene developers who work on both projects). For that reason, I don't think we should change the name. |
}); | ||
precommit.configure(t -> t.dependsOn(task)); | ||
|
||
project.getPluginManager().withPlugin("java", p -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion we should extract that into a JavaPreCommit plugin. this also ensure this code is not executed more than once per project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not doing anything extra, there is no java precommit task. This is only ensuring when we have java projects with precommit, we wire up the precommit task in the desired order. I can move it so it is only applied once.
got it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm 👍
Each precommit task is currently registered inside the shared PrecommitTasks class. Having a single class with all precommit tasks makes individualizing which precommit tasks are needed based on type of project difficult, where we often just disable somet tasks eg for all qa projects. This commit creates plugins for each precommit task, and moves the configuration of the task into each plugin. PrecommitTasks remains for now, and just delegates to each plugin, but will be removed in a future change.
Each precommit task is currently registered inside the shared PrecommitTasks class. Having a single class with all precommit tasks makes individualizing which precommit tasks are needed based on type of project difficult, where we often just disable somet tasks eg for all qa projects. This commit creates plugins for each precommit task, and moves the configuration of the task into each plugin. PrecommitTasks remains for now, and just delegates to each plugin, but will be removed in a future change.
Each precommit task is currently registered inside the shared PrecommitTasks class. Having a single class with all precommit tasks makes individualizing which precommit tasks are needed based on type of project difficult, where we often just disable somet tasks eg for all qa projects. This commit creates plugins for each precommit task, and moves the configuration of the task into each plugin. PrecommitTasks remains for now, and just delegates to each plugin, but will be removed in a future change.
Each precommit task is currently registered inside the shared PrecommitTasks class. Having a single class with all precommit tasks makes individualizing which precommit tasks are needed based on type of project difficult, where we often just disable somet tasks eg for all qa projects. This commit creates plugins for each precommit task, and moves the configuration of the task into each plugin. PrecommitTasks remains for now, and just delegates to each plugin, but will be removed in a future change.
Each precommit task is currently registered inside the shared
PrecommitTasks class. Having a single class with all precommit tasks
makes individualizing which precommit tasks are needed based on type of
project difficult, where we often just disable somet tasks eg for all qa
projects. This commit creates plugins for each precommit task, and moves
the configuration of the task into each plugin. PrecommitTasks remains
for now, and just delegates to each plugin, but will be removed in a
future change.