Coder Social home page Coder Social logo

quarkiverse / quarkus-github-app Goto Github PK

View Code? Open in Web Editor NEW
60.0 3.0 28.0 4.5 MB

Develop your GitHub Apps in Java with Quarkus.

Home Page: https://docs.quarkiverse.io/quarkus-github-app/dev/index.html

License: Apache License 2.0

Java 93.46% CSS 0.13% HTML 1.83% JavaScript 4.58%
quarkus-extension

quarkus-github-app's Introduction

Quarkus GitHub App

Version

All Contributors

Develop your GitHub Apps in Java with Quarkus

Tip

Interested in GitHub Actions? Have a look at the Quarkus GitHub Action extension.

Quarkus GitHub App is a Quarkus extension that allows to create GitHub Apps in Java with very little boilerplate.

Think of it as Probot for Java with some cool additional features.

And yes, it supports generating native executables with GraalVM or Mandrel.

Your application will look like:

class MyGitHubApp {

	void onOpen(@Issue.Opened GHEventPayload.Issue issuePayload) throws IOException {
		issuePayload.getIssue().comment("Hello from MyGitHubApp");
	}
}

And that's it.

The code above listens to the issues.opened GitHub event and posts a comment in each opened issue.

That's for the basics but it supports the GitHub REST API, execution of GraphQL queries, YAML or JSON config files in your repository...

It relies on a Smee.io proxy during the development of the app to redirect GitHub events towards your local instance with no hassle.

With the command-airline additional extension, you can also easily develop comment-based commands (e.g. @bot do something).

Finally, it comes with a nice and searchable Replay UI:

Replay UI

Compatibility

Quarkus GitHub App 2.x is compatible with Quarkus 3.x.

We recommend using the latest versions of both for the best experience.

Documentation

To get you started (and more!), please refer to the extensive documentation.

Anything unclear or missing in the documentation? Please open an issue, we will address it quickly.

Examples

How?

The Quarkus GitHub App extension uses the Hub4j GitHub API to parse the webhook payloads and handle the GitHub REST API calls.

It can also execute GraphQL queries towards the GitHub GraphQL API via the SmallRye GraphQL Client.

The rest of the extension is Quarkus magic - mostly code generation with Gizmo - to get everything wired.

It also leverages Reactive Routes, CDI events (both sync and async), and Caffeine.

Architecture

Status

This extension is considered stable and is used in production.

License

This project is licensed under the Apache License Version 2.0.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Guillaume Smet
Guillaume Smet

💻 🚧
Yoann Rodière
Yoann Rodière

💻 🚧
jtama
jtama

💻
Joël Marty
Joël Marty

💻
Holly Cummins
Holly Cummins

💻
Edoardo Luppi
Edoardo Luppi

🐛
Erin Schnabel
Erin Schnabel

💻
Ryan Dens
Ryan Dens

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

Acknowledgements

Quarkus GitHub App uses the following Open Source projects:

quarkus-github-app's People

Contributors

actions-user avatar allcontributors[bot] avatar dependabot[bot] avatar ebullient avatar gastaldi avatar gsmet avatar holly-cummins avatar joelmarty avatar jtama avatar martinwitt avatar melloware avatar phillip-kruger avatar radtriste avatar rsvoboda avatar ryandens avatar stuartwdouglas avatar tombentley avatar yrodiere avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

quarkus-github-app's Issues

Parallelize action execution?

Not sure it will be compatible with some proper error reporting but it might be a good idea to parallelize the different actions of an event.

What if two methods in my app match the same event?

The docs describe how events result in method calls on my class, but they don't explain what happens if you have two annotated handler methods for events which match the same event.

  1. Do they both get called?
  2. Is there a way I can control which will get called first?

Make the private key optional in tests

Config property quarkus.github-app.private-key is mandatory, but it's not really useful in dev and test mode AFAICT.

People end up setting it to a dummy private key just to make Quarkus happy.

That's inconvenient. Let's make this configuration property optional in tests and dev mode, if possible?

Allow turning off reactions as command execution feedback

Hi everyone! First of all, amazing work on this extension.
Now, I have a command which is quite big internally and performs a lot of checks, which then turn into a big informational message.

Even though the command may have failed, or simply aborted, the framework still react with PLUS_ONE.
To avoid that, I have to throw a random exception (which is not really cool).
But still with that, a MINUS_ONE is used, and it could not be appropriate for the usecase.

Is there a way to completely turn off this reactions feature?

Failure when parameter for the config file is first

While working on my xunit-reporter Quarkus based bot, I added customization through a config file.
So I changed my method from:

void onWorkflow(@WorkflowRun.Completed GHEventPayload.WorkflowRun workflowRunPayload) throws IOException

to:

void onWorkflow(@ConfigFile(CONFIG_FILE_NAME) XUnitReporterConfigFile config, @WorkflowRun.Completed GHEventPayload.WorkflowRun workflowRunPayload) throws IOException

But quarkus:dev now reports errors:

2021-04-22 11:57:40,170 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build f
ailed due to errors
        [error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedReso
lutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$WorkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:1081)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:255)
        at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:129)
        at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:419)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:920)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$WorkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:484)
        at io.quarkus.arc.processor.ObserverInfo.init(ObserverInfo.java:263)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:250)
        ... 12 more

        at io.quarkus.runner.bootstrap.AugmentActionImpl.runAugment(AugmentActionImpl.java:396)
        at io.quarkus.runner.bootstrap.AugmentActionImpl.createInitialRuntimeApplication(AugmentActionImpl.java:269)
        at io.quarkus.runner.bootstrap.AugmentActionImpl.createInitialRuntimeApplication(AugmentActionImpl.java:66)
        at io.quarkus.deployment.dev.IsolatedDevModeMain.firstStart(IsolatedDevModeMain.java:79)
        at io.quarkus.deployment.dev.IsolatedDevModeMain.accept(IsolatedDevModeMain.java:378)
        at io.quarkus.deployment.dev.IsolatedDevModeMain.accept(IsolatedDevModeMain.java:56)
        at io.quarkus.bootstrap.app.CuratedApplication.runInCl(CuratedApplication.java:127)
        at io.quarkus.bootstrap.app.CuratedApplication.runInAugmentClassLoader(CuratedApplication.java:84)
        at io.quarkus.deployment.dev.DevModeMain.start(DevModeMain.java:144)
        at io.quarkus.deployment.dev.DevModeMain.main(DevModeMain.java:63)
Caused by: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
        [error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedReso
lutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$WorkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:1081)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:255)
        at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:129)
        at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:419)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:920)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$WorkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:484)
        at io.quarkus.arc.processor.ObserverInfo.init(ObserverInfo.java:263)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:250)
        ... 12 more

        at io.quarkus.builder.Execution.run(Execution.java:116)
        at io.quarkus.builder.BuildExecutionBuilder.execute(BuildExecutionBuilder.java:79)
        at io.quarkus.deployment.QuarkusAugmentor.run(QuarkusAugmentor.java:153)
        at io.quarkus.runner.bootstrap.AugmentActionImpl.runAugment(AugmentActionImpl.java:394)
        ... 9 more
Caused by: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$Wo
rkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:1081)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:255)
        at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:129)
        at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:419)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:920)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.kohsuke.github.GHEventPayload$WorkflowRun and qualifiers [@Default]
        - java member: me.xunitreporter.XUnitBot_Multiplexer#onWorkflow_5faf52dd037aecb35cbd62097faa01b3382161b8()
        - declared on ObserverInfo [beanClass=me.xunitreporter.XUnitBot_Multiplexer, priority=2500, isAsync=true, reception=ALWAYS, transactionPhase=IN_PROGRESS, observedType=io.quarki
verse.githubapp.runtime.ConfigFileReader, qualifiers=[@Completed]]
        at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:484)
        at io.quarkus.arc.processor.ObserverInfo.init(ObserverInfo.java:263)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:250)
        ... 12 more

2021-04-22 11:57:40,170 INFO  [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start hot replacement endpoint to recover from previous Quarkus startup failure

Having the config file parameter moved after the event payload fixed the issue

Dry-run infrastructure?

Not sure if it's going to be useful if we have a proper test infrastructure but it seems quite related.

The idea would be to somehow log the GH API actions instead of executing them.

Handle projects_v2_item

Discussed in #335

Originally posted by piotrooo July 30, 2022
Hello!
What do you think about add support for projectV2 events?
This will be also related with changes in github-api lib.

Add @Vetoed to event listening classes via an AnnotationTransformer

The event listening classes implemented by the users should not be beans because it's the subclass that should be a bean. Apparently, there is a nice @Vetoed annotation that should work.

We need to check (separately) how it goes with:

  • a scope annotation there @Singleton
  • also in the case of @Startup

With the current code, in both cases, you should have the methods called twice. We should try if adding @Vetoed manually fixes it.

If so, we can implement an AnnotationsTransformer to do it automatically. There is an example in Hibernate Validator for instance: https://github.com/quarkusio/quarkus/blob/main/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java#L291 . The example is a bit complicated in HV, here it should be far easier: we need to push the list of event listening annotations to the transformer and add @Vetoed to the declaring class.

There is an additional step: the @Startup annotation should be copied to the generated subclass if present on the user class. We should also probably override the CDI scope of the generated subclass if a specific scope has been defined on the user class.

AbstractMethodError

After following the great documentation, I wrote a method like this

    void onComment(@IssueComment GHEventPayload.IssueComment commentPayload, GitHub client) throws IOException {
        GHTeam team = client.getOrganization("foo").getTeamByName("bar");
        String body = commentPayload.getComment().getBody();
        if (body.startsWith("/baz")
                && commentPayload.getComment().getUser().isMemberOf(team)) {
            commentPayload.getComment().createReaction(ReactionContent.PLUS_ONE);
        }
    }

which yielded this stacktrace

java.lang.RuntimeException: Error checking value of member method value on interface io.quarkiverse.githubapp.event.IssueComment
	at javax.enterprise.util.AnnotationLiteral.invoke(AnnotationLiteral.java:291)
	at javax.enterprise.util.AnnotationLiteral.getMemberValue(AnnotationLiteral.java:276)
	at javax.enterprise.util.AnnotationLiteral.hashCode(AnnotationLiteral.java:246)
	at java.base/java.util.HashMap.hash(HashMap.java:340)
	at java.base/java.util.HashMap.put(HashMap.java:608)
	at java.base/java.util.HashSet.add(HashSet.java:220)
	at java.base/java.util.Collections.addAll(Collections.java:5506)
	at io.quarkus.arc.impl.EventImpl.select(EventImpl.java:123)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl.dispatch(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass.dispatch$$superforward1(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass.dispatch(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Observer_dispatch_10cbdb5d916a1d0c73e9b215310e5498da0bbaad.notify(Unknown Source)
	at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:320)
	at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:298)
	at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:73)
	at io.quarkiverse.githubapp.runtime.Routes.handleRequest(Routes.java:128)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass.handleRequest$$superforward1(Unknown Source)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass$$function$$2.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass.handleRequest(Unknown Source)
	at io.quarkiverse.githubapp.runtime.Routes_RouteHandler_handleRequest_516ab5af9409426c5dbdf44ccd172a61ce906df9.invoke(Unknown Source)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:97)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:22)
	at io.vertx.ext.web.impl.BlockingHandlerDecorator.lambda$handle$0(BlockingHandlerDecorator.java:48)
	at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
	at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at javax.enterprise.util.AnnotationLiteral.invoke(AnnotationLiteral.java:288)
	... 47 more
Caused by: java.lang.AbstractMethodError: Receiver class io.quarkiverse.githubapp.event.IssueComment_AnnotationLiteral does not define or inherit an implementation of the resolved method 'abstract java.lang.String value()' of interface io.quarkiverse.githubapp.event.IssueComment.
	... 52 more

Command-level configuration is never taken into consideration

As you can see the command class name is the Quarkus-created one, not the original one.
This means commandConfigs.getOrDefault will always return the default one, from the @Cli.
Same for CommandPermissionConfig or CommandTeamConfig.

image

image

This is pretty severe imho.

Explain how @ConfigFile works with GitHubAppTesting

I'm trying to use the GitHubAppTesting example (https://quarkiverse.github.io/quarkiverse-docs/quarkus-github-app/dev/testing.html#_create_a_test_class) for a bot with a method like this:

public void onIssueComment(
            @IssueComment.Created
            @IssueComment.Edited
            GHEventPayload.IssueComment commentPayload,
            @ConfigFile("bot.yml") BotConfig config) throws IOException {

The config argument is being passed as null. I don't really want to change my business logic to allow a null config, I really want to be able to use a yaml for the config to be used by the test. The docs don't explain how to do this. Is it possible?

Support GraphQL client

The state of the GitHub API is a bit inconsistent: some actions can only be done with the REST API while some others are only accessible from the GraphQL API (typically the discussion API has more features in GraphQL).

Thus we should probably wire a GraphQL client properly configured that people could use. Note that we won't implement the GitHub GraphQL API itself but provide a facility that people can use to write their GraphQL queries.

Improve error reporting and reliability

Ideally when one action fails, it should only affect this action.

We should log errors with some context: delivery id + some information coming from the payload (repository#issuenumber for instance) + event.action + failing method.

Also it would be nice if we could propagate the errors to the HTTP response so that they are included in the GitHub UI.

Test payload signature check

We cannot test it with Smee.io but if we can download an original payload from GitHub UI, it might be testable.

Add a <description> to the runtime artifacts´ pom.xml

The generated metadata is reusing the <description> from the parent pom. It should have its own:

    {
      "name": "Quarkus - GitHub App - Runtime",
      "description": "Parent POM for Quarkiverse projects that includes the default release and artifact publishing related\n    configuration",
    }

Also it would be nice to change the name to drop the - Runtime or add a quarkus-extension.yaml to your runtime project

Event sent twice

I started a GitHub app that would listen to workflow completion.
So here's my method:

    void onWorkflow(@WorkflowRun.Completed GHEventPayload.WorkflowRun workflowRunPayload) {
        System.out.println("Workflow completed id " + workflowRunPayload.getWorkflowRun().getWorkflowId());
        System.out.println("sha1 " + workflowRunPayload.getWorkflowRun().getHeadSha());
    }

But I can see that this method is triggered twice for each workflow run. I can see the events duplicated in the replay log.
I also checked the Smee log and can see that for each workflow run I see both POST request one for started and one for completed

`org.kohsuke.github.GHPullRequest$AutoMerge` does not seem to be properly added to the native build

When the application is using native build and tries to work with a PR which has the Auto Merge feature enabled, it failes to deserialize the PR:

Exception: java.util.concurrent.CompletionException: org.kohsuke.github.HttpException: Server returned HTTP response code: 200, message: 'null' for URL: https://api.github.com/repos/scholzj/test-repo/pulls/13
	at io.quarkus.arc.impl.EventImpl.handleExceptions(EventImpl.java:205)
	at io.quarkus.arc.impl.EventImpl$1.get(EventImpl.java:102)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:829)
	at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596)
	at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Caused by: org.kohsuke.github.HttpException: Server returned HTTP response code: 200, message: 'null' for URL: https://api.github.com/repos/scholzj/test-repo/pulls/13
	at org.kohsuke.github.GitHubClient.interpretApiError(GitHubClient.java:548)
	at org.kohsuke.github.GitHubClient.sendRequest(GitHubClient.java:401)
	at org.kohsuke.github.GitHubClient.sendRequest(GitHubClient.java:355)
	at org.kohsuke.github.Requester.fetch(Requester.java:76)
	at org.kohsuke.github.GHRepository.getPullRequest(GHRepository.java:1504)
	at cz.scholz.milestonecheck.MilestoneCheck.onIssue(MilestoneCheck.java:35)
	at cz.scholz.milestonecheck.MilestoneCheck_Multiplexer.onIssue_b81ad9dedcc11b629dc5c57b612dce49f275822b(Unknown Source)
	at cz.scholz.milestonecheck.MilestoneCheck_Multiplexer_Observer_onIssue_b81ad9dedcc11b629dc5c57b612dce49f275822b_8bddb47cb393d69610f6304049ad502bf5a86680.notify(Unknown Source)
	at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:320)
	at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:302)
	at io.quarkus.arc.impl.EventImpl$1.get(EventImpl.java:101)
	... 10 more
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.kohsuke.github.GHPullRequest$AutoMerge` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"url":"https://api.github.com/repos/scholzj/test-repo/pulls/13","id":905219516,"node_id":"PR_kwDOCcuZqM419I28","html_url":"https://github.com/scholzj/test-repo/pull/13","diff_url":"https://github.com/scholzj/test-repo/pull/13.diff","patch_url":"https://github.com/scholzj/test-repo/pull/13.patch","issue_url":"https://api.github.com/repos/scholzj/test-repo/issues/13","number":13,"state":"open","locked":false,"title":"Update README.md","user":{"login":"scholzj","id":5658439,"node_id":"MDQ6VXNlcjU2"[truncated 15221 chars]; line: 1, column: 14488] (through reference chain: org.kohsuke.github.GHPullRequest["auto_merge"])
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1349)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:391)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
	at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2051)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1492)
	at org.kohsuke.github.GitHubResponse.parseBody(GitHubResponse.java:88)
	at org.kohsuke.github.Requester.lambda$fetch$1(Requester.java:76)
	at org.kohsuke.github.GitHubClient.createResponse(GitHubClient.java:485)
	at org.kohsuke.github.GitHubClient.sendRequest(GitHubClient.java:387)
	... 19 more

This seems to be because the org.kohsuke.github.GHPullRequest$AutoMerge class is not properly added to the native build (everything seems to work fine when not using the native build).

I was able to workaround the issue by adding this class through the ReflectionConfigurationFiles option. But I wonder if this is something what should be set by default and work out of the box.

Installation Created: event triggers a NullPointerException

Before: this extension is absolutely great! Pretty much liked it! Congrats! 👍 🎉

Now, let me get to the issue:

I tried to get a Installation.Created event, but then I stumbled across the following exception:

Exception: java.lang.NullPointerException: Missing instance URL!
	at java.base/java.util.Objects.requireNonNull(Objects.java:247)
	at org.kohsuke.github.GHRepository.populate(GHRepository.java:3260)
	at org.kohsuke.github.GHEventPayload$Installation.lateBind(GHEventPayload.java:276)
	at org.kohsuke.github.GitHub.parseEventPayload(GitHub.java:944)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl.dispatch(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass.dispatch$$superforward1(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Subclass.dispatch(Unknown Source)
	at io.quarkiverse.githubapp.GitHubEventDispatcherImpl_Observer_dispatch_2d7663abb168c3cf472f4244f593b64c986877c4.notify(Unknown Source)
	at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:320)
	at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:298)
	at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:73)
	at io.quarkiverse.githubapp.runtime.Routes.handleRequest(Routes.java:128)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass.handleRequest$$superforward1(Unknown Source)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.quarkiverse.githubapp.runtime.Routes_Subclass.handleRequest(Unknown Source)
	at io.quarkiverse.githubapp.runtime.Routes_RouteHandler_handleRequest_516ab5af9409426c5dbdf44ccd172a61ce906df9.invoke(Unknown Source)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:97)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:22)
	at io.vertx.ext.web.impl.BlockingHandlerDecorator.lambda$handle$0(BlockingHandlerDecorator.java:48)
	at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
	at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:830)

This is most likely happening when trying to populate data from the GitHub repositories JSON object:

image

Maybe it doesn't find the url JSON attribute and then it breaks on this line:

image

Is there a workaround for this? Maybe a config to avoid this problem?

Make sure dev mode works correctly

I haven't tested it explicitly but I had some weird behaviors while developing. Not sure it works very well with the smee.io forwarding.

We might need something similar to what was done for Kafka Streams.

DynamicGraphQLClient never gets closed

This code creates a client:

ResultHandle gitHubGraphQLClientRh = tryBlock.loadNull();
if (dispatchingConfiguration.requiresGraphQLClient()) {
gitHubGraphQLClientRh = tryBlock.invokeVirtualMethod(
MethodDescriptor.ofMethod(GitHubService.class, "getInstallationGraphQLClient", DynamicGraphQLClient.class,
long.class),
tryBlock.readInstanceField(
FieldDescriptor.of(dispatcherClassCreator.getClassName(), GITHUB_SERVICE_FIELD,
GitHubService.class),
tryBlock.getThis()),
installationIdRh);
}

By calling this:

@Override
public DynamicGraphQLClient getInstallationGraphQLClient(long installationId) {
try {
return createInstallationGraphQLClient(installationId);
} catch (IOException | ExecutionException | InterruptedException e1) {
synchronized (this) {
try {
// retry in a synchronized in case the token is invalidated in another thread
return createInstallationGraphQLClient(installationId);
} catch (IOException | ExecutionException | InterruptedException e2) {
try {
// this time we invalidate the token entirely and go for a new token
installationTokenCache.invalidate(installationId);
return createInstallationGraphQLClient(installationId);
} catch (IOException | ExecutionException | InterruptedException e3) {
throw new IllegalStateException(
"Unable to create a GitHub GraphQL client for the installation " + installationId, e3);
}
}
}
}
}

private DynamicGraphQLClient createInstallationGraphQLClient(long installationId)
throws IOException, ExecutionException, InterruptedException {
CachedInstallationToken installationToken = installationTokenCache.get(installationId);
DynamicGraphQLClient graphQLClient = DynamicGraphQLClientBuilder.newBuilder()
.url(gitHubAppRuntimeConfig.instanceEndpoint + "/graphql")
.header(AUTHORIZATION_HEADER, String.format(AUTHORIZATION_HEADER_BEARER, installationToken.getToken()))
.build();
// this call is probably - it's not documented - not counted in the rate limit
graphQLClient.executeSync("query {\n" +
"rateLimit {\n" +
" limit\n" +
" cost\n" +
" remaining\n" +
" resetAt\n" +
" }\n" +
"}");
return graphQLClient;
}

But as far as I can tell, there is absolutely no code to call the close() method on the created client.

Maybe we should just close it after all events have been processed? (careful though, it's reactive code so it's non-trivial to say when processing ends exactly...)

Maybe it should be cached like the non-graphQL client, and renewed every 10 minutes, and closed after 20 minutes?

Allow usage with private github instances

Trying to use this project to build a bot for our private github instance.

It looks like it is currently impossible to specify a specific github host.

I followed the setup guide and when running the app in dev mode, I get the following error (stripped some stacktrace elements):

Exception: java.lang.IllegalStateException: Unable to create a GitHub token for the installation 959
        at io.quarkiverse.githubapp.runtime.github.GitHubService$CreateInstallationToken.load(GitHubService.java:117)
        at io.quarkiverse.githubapp.runtime.github.GitHubService$CreateInstallationToken.load(GitHubService.java:105)
        at com.github.benmanes.caffeine.cache.LocalLoadingCache.lambda$newMappingFunction$2(LocalLoadingCache.java:141)
        at com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$14(BoundedLocalCache.java:2413)
        at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
        at com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2411)
        at com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2394)
        at com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:108)
        at com.github.benmanes.caffeine.cache.LocalLoadingCache.get(LocalLoadingCache.java:54)
        at io.quarkiverse.githubapp.runtime.github.GitHubService.createInstallationClient(GitHubService.java:93)
        ... more
Caused by: org.kohsuke.github.GHFileNotFoundException: https://api.github.com/app {"message":"Integration must generate a public key","documentation_url":"https://docs.github.com/rest"}
        at org.kohsuke.github.GitHubClient.interpretApiError(GitHubClient.java:480)
        at org.kohsuke.github.GitHubClient.sendRequest(GitHubClient.java:407)
        at org.kohsuke.github.GitHubClient.sendRequest(GitHubClient.java:355)
        at org.kohsuke.github.Requester.fetch(Requester.java:76)
        at org.kohsuke.github.GitHub.getApp(GitHub.java:1109)
        at io.quarkiverse.githubapp.runtime.github.GitHubService$CreateInstallationToken.load(GitHubService.java:111)
        ... 61 more
Caused by: java.io.FileNotFoundException: https://api.github.com/app
        at org.kohsuke.github.extras.okhttp3.ObsoleteUrlFactory$OkHttpURLConnection.getInputStream(ObsoleteUrlFactory.java:490)
        at org.kohsuke.github.extras.okhttp3.ObsoleteUrlFactory$DelegatingHttpsURLConnection.getInputStream(ObsoleteUrlFactory.java:1204)
        at org.kohsuke.github.GitHubHttpUrlConnectionClient$HttpURLConnectionResponseInfo.bodyStream(GitHubHttpUrlConnectionClient.java:196)
        at org.kohsuke.github.GitHubResponse$ResponseInfo.getBodyAsString(GitHubResponse.java:314)
        at org.kohsuke.github.GitHubResponse.parseBody(GitHubResponse.java:92)
        at org.kohsuke.github.Requester.lambda$fetch$1(Requester.java:76)
        at org.kohsuke.github.GitHubClient.createResponse(GitHubClient.java:449)

My guess is that the issue lies in the GitHubService class, in the createInstallationClient method where we could inject an endpoint url and call the GithubBuilder.withEndpoint() method.

I will try to make a PR for this.

Properly document usage

We should document everything properly:

  • how to set up the application on GH
  • how to develop one

Once Quarkus 1.11 is released, use quarkus-smallrye-jwt-build

public String createJwtToken(String githubAppId, PrivateKey privateKey, long ttlMillis)
            throws GeneralSecurityException, IOException {
       // RS256 is default
       // issued at is set to default to now()
       return Jwt.issuer(githubAppId).expiresAt(ttlMillis).sign(privateKey);

Allow editing events before replaying them in the replay UI

This could come in handy to simulate things without having to use the GitHub UI to actually trigger events; e.g. simulate an action performed by another user.

I would just open a popup with a textarea and let people deal with the JSON syntax and GitHub event structure, no need for more advanced tooling IMO.

Not all parsing errors can be outputted as comments

Let's say I get this exception:

Exception: java.lang.IllegalArgumentException: unbalanced quotes in @bot git cherry-pick -to "my-bran" c"
	at io.quarkiverse.githubapp.command.airline.runtime.util.Commandline.translateCommandline(Commandline.java:98)
	at io.quarkiverse.githubapp.command.airline.runtime.AbstractCommandDispatcher.getCommand(AbstractCommandDispatcher.java:73)
	at com.lppedd.app.commands.AppCliCommandDispatcherImpl.dispatch(Unknown Source)
	at com.lppedd.app.commands.AppCliCommandDispatcherImpl_Multiplexer_Subclass.dispatch$$superforward1(Unknown Source)
	at com.lppedd.app.commands.AppCliCommandDispatcherImpl_Multiplexer_Subclass$$function$$8.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:53)

The error goes through DefaultErrorHandler, and it's simply logged.
Is there a way to allow a custom error handler to reply to the issue/PR with the error message?

Throw an error if the event listening class is annotated with `@Startup`

If a class is annotated with io.quarkus.runtime.Startup, it will be made a bean.

We do not want the event listening class to be a bean as it will be subclassed and it's the subclass that should be a bean.

Typically, this can lead to unexpected behavior when having a @PostConstruct annotation.

Wrap in backticks the command in case of internal parse errors

Follow up to #366.

Outputting the original command as code avoids a tag and it's also easier to read.
In the following case the error message comes from an exception. A simple replace should work imo.

image

In my case I've got a customized error message, using

parseErrorStrategy = ParseErrorStrategy.COMMENT_MESSAGE_HELP_ERRORS,
parseErrorMessage = "> `%s`\n\n:warning: Unable to parse the command.",

So don't mind the duplication.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.