Developer Reference

Events

Listening to events

To listen to an event, you need to create a method with one of the event annotations:

class CreateComment {

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

A few observations:

  • The method may either be package-protected or public.

  • Most of the GitHub API methods throw IOExceptions so your methods need to propagate it. We have some nice error handling.

  • The payload type needs to be consistent with the event type.

A method may listen to several events as long as they share the same payload type:

class IssueListener {

	void onOpenOrEdit(@Issue.Opened @Issue.Edited GHEventPayload.Issue issuePayload) {
		// do something
	}
}

Several methods can listen to the same event types but you cannot control the order of their execution. We use CDI events under the hood and the execution of event observer methods cannot be ordered. If you need ordering for several steps, use a single method to control the execution of these steps. They can be split in smaller private methods if need be.

Event types

Here are all the events currently supported, together with the type of the payload that will be injected.

GitHub Event Type Events Payload

check_run

@CheckRun.Completed, @CheckRun.Created, @CheckRun.RequestedAction, @CheckRun.Rerequested

GHEventPayload.CheckRun

check_suite

@CheckSuite.Completed, @CheckSuite.Requested, @CheckSuite.Rerequested

GHEventPayload.CheckSuite

commit_comment

@CommitComment.Created

GHEventPayload.CommitComment

create

@Create

GHEventPayload.Create

delete

@Delete

GHEventPayload.Delete

deployment

@Deployment.Created

GHEventPayload.Deployment

deployment_status

@DeploymentStatus.Created

GHEventPayload.DeploymentStatus

discussion

@Discussion.Answered, @Discussion.CategoryChanged, @Discussion.Closed, @Discussion.Created, @Discussion.Deleted, @Discussion.Edited, @Discussion.Labeled, @Discussion.Locked, @Discussion.Pinned, @Discussion.Reopened, @Discussion.Transferred, @Discussion.Unanswered, @Discussion.Unlabeled, @Discussion.Unlocked, @Discussion.Unpinned

GHEventPayload.Discussion

discussion_comment

@DiscussionComment.Created, @DiscussionComment.Deleted, @DiscussionComment.Edited

GHEventPayload.DiscussionComment

fork

@Fork

GHEventPayload.Fork

installation

@Installation.Created, @Installation.Deleted, @Installation.NewPermissionsAccepted, @Installation.Suspend, @Installation.Unsuspend

GHEventPayload.Installation

installation_repositories

@InstallationRepositories.Added, @InstallationRepositories.Removed

GHEventPayload.InstallationRepositories

issues

@Issue.Assigned, @Issue.Closed, @Issue.Deleted, @Issue.Demilestoned, @Issue.Edited, @Issue.Labeled, @Issue.Locked, @Issue.Milestoned, @Issue.Opened, @Issue.Pinned, @Issue.Reopened, @Issue.Transferred, @Issue.Unassigned, @Issue.Unlabeled, @Issue.Unlocked, @Issue.Unpinned

GHEventPayload.Issue

issue_comment

@IssueComment.Created, @IssueComment.Deleted, @IssueComment.Edited

GHEventPayload.IssueComment

label

@Label.Created, @Label.Deleted, @Label.Edited

GHEventPayload.Label

member

@Member.Added, @Member.Edited, @Member.Removed

GHEventPayload.Member

membership

@Membership.Added, @Membership.Removed

GHEventPayload.Membership

ping

@Ping

GHEventPayload.Ping

public

@Public

GHEventPayload.Public

pull_request

@PullRequest.Assigned, @PullRequest.Closed, @PullRequest.Edited, @PullRequest.Labeled, @PullRequest.Locked, @PullRequest.Opened, @PullRequest.ReadyForReview, @PullRequest.Reopened, @PullRequest.ReviewRequested, @PullRequest.ReviewRequestRemoved, @PullRequest.Synchronize, @PullRequest.Unassigned, @PullRequest.Unlabeled, @PullRequest.Unlocked

GHEventPayload.PullRequest

pull_request_review

@PullRequestReview.Dismissed, @PullRequestReview.Edited, @PullRequestReview.Submitted

GHEventPayload.PullRequestReview

pull_request_review_comment

@PullRequestReviewComment.Created, @PullRequestReviewComment.Deleted, @PullRequestReviewComment.Edited

GHEventPayload.PullRequestReviewComment

pull_request_target (only for actions)

@PullRequestTarget.Assigned, @PullRequestTarget.Closed, @PullRequestTarget.Edited, @PullRequestTarget.Labeled, @PullRequestTarget.Locked, @PullRequestTarget.Opened, @PullRequestTarget.ReadyForReview, @PullRequestTarget.Reopened, @PullRequestTarget.ReviewRequested, @PullRequestTarget.ReviewRequestRemoved, @PullRequestTarget.Synchronize, @PullRequestTarget.Unassigned, @PullRequestTarget.Unlabeled, @PullRequestTarget.Unlocked

GHEventPayload.PullRequest

push

@Push

GHEventPayload.Push

release

@Release.Created, @Release.Deleted, @Release.Edited, @Release.Prereleased, @Release.Published, @Release.Released, @Release.Unpublished

GHEventPayload.Release

repository

@Repository.Archived, @Repository.Created, @Repository.Deleted, @Repository.Edited, @Repository.Privatized, @Repository.Publicized, @Repository.Renamed, @Repository.Transferred, @Repository.Unarchived

GHEventPayload.Repository

status

@Status

GHEventPayload.Status

team_add

@TeamAdd

GHEventPayload.TeamAdd

team

@Team.AddedToRepository, @Team.Created, @Team.Deleted, @Team.Edited, @Team.RemovedFromRepository

GHEventPayload.Team

workflow_dispatch

@WorkflowDispatch

GHEventPayload.WorkflowDispatch

workflow_run

@WorkflowRun.Completed, @WorkflowRun.Requested

GHEventPayload.WorkflowRun

If you want to access the low level JSON payload, you can use the raw GitHubEvent, either by substituting it to the API payload:

class TriageIssue {

    void triageIssue(@Issue.Opened GitHubEvent gitHubEvent) {
        // do something
    }
}

Or by adding it as an additional parameter:

class TriageIssue {

    void triageIssue(@Issue.Opened GHEventPayload.Issue issuePayload, GitHubEvent gitHubEvent) {
        // do something
    }
}

The GitHubEvent exposes the raw JSON either as a string (via GitHubEvent#getPayload()) or as a Vert.x JsonObject (via GitHubEvent#getParsedPayload()), together with some additional information like the installation id, the event or the action.

If you miss an event type, it is also possible to listen to events by using the @RawEvent annotation. The @RawEvent annotation allows you to listen to all events/actions, and you may also specify the event and the action you want to listen to.

When using the @RawEvent annotation, the only allowed payload parameter type is GitHubEvent.

class ActOnDeploymentProtectionRule {

    void onDeploymentProtectionRule(@RawEvent(event = "deployment_protection_rule", action = "requested") GitHubEvent gitHubEvent) {
        // do something
    }
}

Both event and action are optional and you can listen to all events by using the following code:

class CatchAll {

    void catchAll(@RawEvent GitHubEvent gitHubEvent) {
        // do something
    }
}

If you miss an event type, please report it by creating an issue. We try to add to contribute to the Hub4j GitHub API all the event types that are useful.

When missing an event type, there are two options:

Configuration file

For some usage, you might need to include a configuration file that is repository-specific and versioned.

The Quarkus GitHub App extension supports the following features:

  • Automatic injection of config files into your methods.

  • YAML, JSON or text config files.

  • Automatic deserialization of your YAML or JSON config files into Java POJOs using Jackson.

Injecting a configuration file in your method is as simple as:

class TriageIssue {

    void triageIssue(@Issue.Opened GHEventPayload.Issue issuePayload,
            @ConfigFile("quarkus-bot-java.yml") QuarkusBotConfigFile quarkusBotConfigFile) {
        // do something
    }
}

The configuration file .github/quarkus-bot-java.yml present in the default branch of the repository for which the event has been triggered is parsed and deserialized to a QuarkusBotConfigFile instance using Jackson.

If the file does not exist in the repository, quarkusBotConfigFile will be null.

If you want to get the content of the configuration file as is, use a String.

Note that @ConfigFile injection supports using Optional<YourConfigType>.

If your repository is private, reading configuration files requires your GitHub App to have the Contents Read permission.

By default, the config file path is relative to the .github/ directory.

You can reference a file outside this directory by using an /absolute/path.

Error handler

The Quarkus GitHub App extension provides an error handler that will log errors with as many details as possible.

You can customize the error handler by creating a CDI bean implementing io.quarkiverse.githubapp.error.ErrorHandler:

Some errors may be triggered before the payload has been parsed. In this case, the payload parameter passed to the handleError() method is null.

Injecting a GitHub instance

When you need to access the authenticated GitHub instance, for instance to call GitHub#getMyself(), simply inject it into your method:

class TriageIssue {

    void triageIssue(@Issue.Opened GHEventPayload.Issue issuePayload, GitHub gitHub) {
        gitHub.getMyself();
    }
}

The injected GitHub instance is authenticated as an installation.

Injecting a GraphQL client

For some purposes, using the GitHub GraphQL API might get handy (typically to access the Discussions API that is only available in the GraphQL API). In the same way you can inject an authenticated GitHub instance, you can inject an authenticated DynamicGraphQLClient as follows:

class TriageIssue {

    void triageIssue(@Issue.Opened GHEventPayload.Issue issuePayload, DynamicGraphQLClient gitHubGraphQLClient) {
        // do something GraphQLy with gitHubGraphQLClient
    }
}

The injected DynamicGraphQLClient instance is authenticated as an installation.

DynamicGraphQLClient is a dynamic SmallRye GraphQL client. You can find more information about the SmallRye GraphQL client here and here.

Webhook events

While Quarkus GitHub App was primarily designed as a tool to develop GitHub Apps, it is also possible to use it to handle your webhook requests.

It will take care of all the ceremony of authenticating the requests and will save you a lot of boilerplate.

There are a few differences though:

  • You will have to use @RawEvent to listen to the events and get the raw JSON of the payload.

  • Webhook requests don’t provide an installation id so we can’t initialize an installation GitHub client nor a GraphQL client.

The default GitHub REST client instance that can be injected is an application client and has so few permissions that it is not really useful.

While it could be a major inconvenience, we present a nice feature dedicated to this use case in the following section.

Providing a personal access token

When using Quarkus GitHub App, in most cases, the REST and GraphQL clients provided by the installation are what you are looking for: they have the permissions allowed for this GitHub App and it should be enough to do what your GitHub App has been designed for.

However, there are corner cases where you might need a client with additional permissions, the most common one is when you deal with webhooks as presented in the previous section.

For this situation, you can define a personal access token by using the quarkus.github-app.personal-access-token configuration property. The personal access token provided in this property will be used to initialize:

  • an authenticated GitHub REST client

  • an authenticated DynamicGraphQL GraphQL client

These clients will be automatically injected in your methods when injecting clients if the payload doesn’t provide an installation id (if it does provide one, the regular installation clients will be injected).

It is also possible to directly obtain the clients authenticated with the personal access token by injecting the TokenGitHubClients CDI bean. It provides methods to get the authenticated REST and GraphQL clients.

Credentials provider

Quarkus GitHub App supports the usage of a CredentialsProvider to provide some of the configuration properties that you might not want to appear in a configuration file.

You can find all the information you need about CredentialsProviders in the dedicated guide.

The basic principle is that you provide a CDI bean that will implement the CredentialsProvider and will provide the values for the supported configuration properties. It can either be provided by the Vault extension if you are using HashiCorp Vault or you can provide you own custom CredentialsProvider implementation.

Using a CredentialsProvider is easy:

  • Make sure you have an implementation of CredentialsProvider and that it is a @Singleton or @ApplicationScoped CDI bean.

  • Set the quarkus.github-app.credentials-provider to define the name of the credentials provider. See it as the name of a keyring containing your Quarkus GitHub App secrets.

  • For the given name, your CredentialsProvider may return a Map containing entries for githubAppPrivateKey and/or githubAppWebhookSecret. You can use the constants present in io.quarkiverse.githubapp.Credentials for practicality.

That is all you need to do: Quarkus GitHub App will then automatically use the values provided by the CredentialsProvider for the private key and the webhook secret.

Customizing the GitHub clients

You can customize the GitHub clients initialized by Quarkus GitHub App by creating a CDI bean implementing io.quarkiverse.githubapp.GitHubCustomizer.

For instance:

@Singleton
public class MyGitHubCustomizer implements GitHubCustomizer {

    @Override
    public void customize(GitHubBuilder builder) {
       // call methods of the builder
    }
}

This will apply the customizations to both the application clients and the installation clients.

However, some customizations can’t be applied to the application clients such as configuring a rate limit checker. You may customize the application clients differently by implementing customizeApplicationClient(GitHubBuilder):

@Singleton
public class MyGitHubCustomizer implements GitHubCustomizer {

    @Override
    public void customize(GitHubBuilder builder) { (1)
       // call methods of the builder
    }

    @Override
    public void customizeApplicationClient(GitHubBuilder builder) { (2)
       // call methods of the builder
    }
}
1 Customize the installation clients.
2 Customize the application clients.
About application and installation clients

A GitHub App relies on two types of GitHub clients:

  • The application client: it is authenticated as the application, it has very few permissions and is mostly used to create installation-specific tokens.

  • The installation client: it is created specifically for an installation of the application (i.e. it is tied where the application is installed) and it is the client you consume in your GitHub App code. It has the permissions you defined for your GitHub App.

Configuration Reference

The Quarkus GitHub App extension exposes the following configuration properties:

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The numeric application id provided by GitHub.

Optional for tests, but mandatory in production and dev mode.

Environment variable: QUARKUS_GITHUB_APP_APP_ID

string

The GitHub name of the application.

Optional, only used for improving the user experience.

Environment variable: QUARKUS_GITHUB_APP_APP_NAME

string

Read the configuration files from the source repository in case of a fork.

Environment variable: QUARKUS_GITHUB_APP_READ_CONFIG_FILES_FROM_SOURCE_REPOSITORY

boolean

false

The RSA private key.

Optional for tests, but mandatory in production and dev mode.

Environment variable: QUARKUS_GITHUB_APP_PRIVATE_KEY

PrivateKey

The webhook URL path on which the GitHub App route is mounted.

It defaults to the root / but it can be configured to another path such as /github-events to enable deployment alongside other HTTP routes.

Environment variable: QUARKUS_GITHUB_APP_WEBHOOK_URL_PATH

String

/

The webhook secret if defined in the GitHub UI.

Environment variable: QUARKUS_GITHUB_APP_WEBHOOK_SECRET

string

The credentials provider name.

This is the name of the "keyring" containing the GitHub App secrets.

Key names are defined in Credentials.

Environment variable: QUARKUS_GITHUB_APP_CREDENTIALS_PROVIDER

string

The credentials provider bean name.

This is a bean name (as in @Named) of a bean that implements CredentialsProvider. It is used to select the credentials provider bean when multiple exist. This is unnecessary when there is only one credentials provider available.

For Vault, the credentials provider bean name is vault-credentials-provider.

Environment variable: QUARKUS_GITHUB_APP_CREDENTIALS_PROVIDER_NAME

string

The Smee.io proxy URL used when testing locally.

Environment variable: QUARKUS_GITHUB_APP_WEBHOOK_PROXY_URL

string

The GitHub instance endpoint.

Defaults to the public github.com instance.

Environment variable: QUARKUS_GITHUB_APP_INSTANCE_ENDPOINT

String

https://api.github.com

The REST API endpoint.

Defaults to the public github.com instance REST API endpoint.

Environment variable: QUARKUS_GITHUB_APP_REST_API_ENDPOINT

String

${quarkus.github-app.instance-endpoint}

The GraphQL API endpoint.

Defaults to the public github.com instance GraphQL endpoint.

Environment variable: QUARKUS_GITHUB_APP_GRAPHQL_API_ENDPOINT

String

${quarkus.github-app.instance-endpoint}/graphql

A personal access token for use with TokenGitHubClients or when no installation id is provided in the payload.

For standard use cases, you will use the installation client which comes with the installation permissions. It can be injected directly in your method.

However, if your payload comes from a webhook and doesn’t have an installation id, it’s handy to be able to use a client authenticated with a personal access token as the application client permissions are very limited.

This token will be used to authenticate the clients provided by TokenGitHubClients and clients authenticated with this personal access token will be automatically provided when injecting GitHub or DynamicGraphQLClient in your method, when the payload doesn’t provide an installation id.

Environment variable: QUARKUS_GITHUB_APP_PERSONAL_ACCESS_TOKEN

string

A directory in which the payloads are saved.

Environment variable: QUARKUS_GITHUB_APP_DEBUG_PAYLOAD_DIRECTORY

path

Architecture Overview

Architecture