func (p *githubTriggerPlugin) HandleGithubWebhook(w http.ResponseWriter, r *http.Request) {
	var err error
	defer func(err *error) {
		if *err == nil {
			return
		}
		log.WithError(*err).Warn("GitHub webhook error")
		http.Error(w, (*err).Error(), http.StatusInternalServerError)
	}(&err)
	if r.Method == "GET" {
		http.Redirect(w, r, "/github?"+r.URL.Query().Encode(), 301)
		return
	}
	payload, err := github.ValidatePayload(r, []byte(p.Config.WebhookSecret))
	if err != nil && strings.Contains(err.Error(), "unknown X-Github-Event") {
		err = nil
		return
	}
	if err != nil {
		return
	}
	event, err := github.ParseWebHook(github.WebHookType(r), payload)
	if err != nil {
		return
	}
	switch event := event.(type) {
	case *github.PushEvent:
		p.processPushEvent(event)
	case *github.InstallationEvent:
		p.processInstallationEvent(event)
	case *github.IssueCommentEvent:
		p.processIssueCommentEvent(r.Context(), event)
	default:
		log.WithField("event", event).Debug("unhandled GitHub event")
		http.Error(w, "unhandled event", http.StatusInternalServerError)
	}
}
HandleGithubWebhook handles incoming Github events
package main
import (
	"context"
	"fmt"
	"net"
	"net/http"
	"reflect"
	"strings"
	"github.com/bradleyfalzon/ghinstallation"
	v1 "github.com/csweichel/werft/pkg/api/v1"
	plugin "github.com/csweichel/werft/pkg/plugin/client"
	"github.com/google/go-github/v35/github"
	log "github.com/sirupsen/logrus"
)
var (
	werftGithubContextPrefix = "ci/werft"
	werftResultChannelPrefix = "github-check-"
	annotationStatusUpdate = "updateGitHubStatus"
	defaultGitHubHost = "github.com"
	commandHelp = `You can interact with werft using: ` + "`" + `/werft command <args>` + "`" + `.
Available commands are:
 - ` + "`" + `/werft run [annotation=value]` + "`" + ` which starts a new werft job from this context.
    You can optionally pass multiple whitespace-separated annotations.
 - ` + "`" + `/werft help` + "`" + ` displays this help
`
)
type Config struct {
	BaseURL string `yaml:"baseURL"`
	WebhookSecret  string `yaml:"webhookSecret"`
	PrivateKeyPath string `yaml:"privateKeyPath"`
	InstallationID int64  `yaml:"installationID,omitempty"`
	AppID          int64  `yaml:"appID"`
	PRComments struct {
		Enabled bool `yaml:"enabled"`
		RequiresOrganisation []string `yaml:"requiresOrg"`
		UpdateComment bool `yaml:"updateComment"`
	} `yaml:"pullRequestComments"`
}
func main() {
	plg := &githubTriggerPlugin{}
	plugin.Serve(&Config{},
		plugin.WithIntegrationPlugin(plg),
		plugin.WithProxyPass(plg),
	)
}
type githubTriggerPlugin struct {
	Config *Config
	Werft  v1.WerftServiceClient
	Github *github.Client
}
func (p *githubTriggerPlugin) Run(ctx context.Context, config interface{}, srv v1.WerftServiceClient) error {
	cfg, ok := config.(*Config)
	if !ok {
		return fmt.Errorf("config has wrong type %s", reflect.TypeOf(config))
	}
	ghtr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, cfg.AppID, cfg.InstallationID, cfg.PrivateKeyPath)
	if err != nil {
		return err
	}
	ghClient := github.NewClient(&http.Client{Transport: ghtr})
	p.Config = cfg
	p.Werft = srv
	p.Github = ghClient
	errchan := make(chan error)
	sub, err := srv.Subscribe(ctx, &v1.SubscribeRequest{})
	if err != nil {
		return fmt.Errorf("cannot subscribe for notification: %w", err)
	}
	log.Infof("status updates for GitHub set up")
	go func() {
		for {
			inc, err := sub.Recv()
			if err != nil {
				errchan <- err
				return
			}
			err = p.updateGitHubStatus(inc.Result)
			if err != nil {
				log.WithError(err).Error("cannot update GitHub status")
			}
		}
	}()
	select {
	case err := <-errchan:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}
func (p *githubTriggerPlugin) updateGitHubStatus(job *v1.JobStatus) error {
	var (
		wantsUpdate   bool
		statusDstRepo string
	)
	for _, a := range job.Metadata.Annotations {
		if a.Key == annotationStatusUpdate {
			wantsUpdate = true
			statusDstRepo = a.Value
			break
		}
	}
	if !wantsUpdate {
		return nil
	}
	var (
		state string
		desc  string
	)
	switch job.Phase {
	case v1.JobPhase_PHASE_PREPARING, v1.JobPhase_PHASE_STARTING, v1.JobPhase_PHASE_RUNNING:
		state = "pending"
		desc = "build is " + strings.TrimPrefix(strings.ToLower(job.Phase.String()), "phase_")
	default:
		if job.Conditions.Success {
			state = "success"
			desc = "The build succeeded!"
		} else {
			state = "failure"
			desc = "The build failed!"
		}
	}
	url := fmt.Sprintf("%s/job/%s", p.Config.BaseURL, job.Name)
	jobGHctx := werftGithubContextPrefix + "/" + job.Metadata.JobSpecName
	ghstatus := &github.RepoStatus{
		State:       &state,
		Description: &desc,
		Context:     &jobGHctx,
		TargetURL:   &url,
	}
	var (
		segs  = strings.Split(statusDstRepo, "/")
		owner string
		repo  string
	)
	if len(segs) == 2 {
		owner, repo = segs[0], segs[1]
	} else {
		owner, repo = job.Metadata.Owner, job.Metadata.Repository.Repo
	}
	log.WithField("status", ghstatus).Debugf("updating GitHub status for %s", job.Name)
	ctx := context.Background()
	_, _, err := p.Github.Repositories.CreateStatus(ctx, owner, repo, job.Metadata.Repository.Revision, ghstatus)
	if err != nil {
		return err
	}
	var idx int
	for _, r := range job.Results {
		var (
			ok    bool
			ghctx string
		)
		for _, c := range r.Channels {
			if c == "github" {
				ok = true
				ghctx = fmt.Sprintf("%s/results/%03d", jobGHctx, idx)
				idx++
				break
			}
			if strings.HasPrefix(c, werftResultChannelPrefix) {
				ok = true
				ghctx = fmt.Sprintf("%s/results/%s", jobGHctx, strings.TrimPrefix(c, werftResultChannelPrefix))
				break
			}
		}
		if !ok {
			continue
		}
		resultURL := url
		if r.Type == "url" {
			resultURL = r.Payload
		}
		success := "success"
		if r.Type == "conclusion" {
			success = r.Payload
		}
		_, _, err := p.Github.Repositories.CreateStatus(ctx,
			owner,
			repo,
			job.Metadata.Repository.Revision,
			&github.RepoStatus{
				State:       &success,
				TargetURL:   &resultURL,
				Description: &r.Description,
				Context:     &ghctx,
			},
		)
		if err != nil {
			log.WithError(err).WithField("job", job.Name).Warn("cannot update result status")
		}
	}
	return nil
}
func (p *githubTriggerPlugin) Serve(ctx context.Context, l net.Listener) error {
	mux := http.NewServeMux()
	mux.HandleFunc("/", http.HandlerFunc(p.HandleGithubWebhook))
	http.Serve(l, mux)
	<-ctx.Done()
	return nil
}