Skip to content

Commit

Permalink
refactor gh_service a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
bartekpacia committed Oct 16, 2024
1 parent 71d5be0 commit dc94c45
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 61 deletions.
4 changes: 2 additions & 2 deletions backend/cmd/gh-updater/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strconv"
"time"

"github.com/bee-ci/bee-ci-system/internal/common/gh_service"
"github.com/bee-ci/bee-ci-system/internal/common/ghservice"

"github.com/bee-ci/bee-ci-system/internal/data"
"github.com/bee-ci/bee-ci-system/internal/updater"
Expand Down Expand Up @@ -59,7 +59,7 @@ func main() {
userRepo := data.NewPostgresUserRepo(db)
repoRepo := data.NewPostgresRepoRepo(db)

githubService := gh_service.NewGithubService(githubAppID, rsaPrivateKey)
githubService := ghservice.NewGithubService(githubAppID, rsaPrivateKey)

minReconnectInterval := 10 * time.Second
maxReconnectInterval := time.Minute
Expand Down
4 changes: 2 additions & 2 deletions backend/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"strconv"
"time"

"github.com/bee-ci/bee-ci-system/internal/common/gh_service"
"github.com/bee-ci/bee-ci-system/internal/common/ghservice"
"github.com/golang-jwt/jwt/v5"

influxdb2 "github.com/influxdata/influxdb-client-go/v2"
Expand Down Expand Up @@ -94,7 +94,7 @@ func main() {
repoRepo := data.NewPostgresRepoRepo(db)
logsRepo := data.NewInfluxLogsRepo(influxClient, influxOrg, influxBucket)

githubService := gh_service.NewGithubService(githubAppID, rsaPrivateKey)
githubService := ghservice.NewGithubService(githubAppID, rsaPrivateKey)
webhooks := webhook.NewWebhookHandler(userRepo, repoRepo, buildRepo, githubService, mainDomain, redirectURL, githubAppClientID, githubAppClientSecret, githubAppWebhookSecret, jwtSecret)
app := api.NewApp(buildRepo, logsRepo, repoRepo, userRepo, jwtSecret)

Expand Down
22 changes: 0 additions & 22 deletions backend/internal/common/auth/bearer_transport.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
// Package auth implements a BearerTransport type that implements the
// http.RoundTripper interface.
package auth

import (
"net/http"
)

type BearerTransport struct {
Token string
Transport http.RoundTripper
}

// RoundTrip implements http.RoundTripper interface.
func (b *BearerTransport) RoundTrip(r *http.Request) (*http.Response, error) {
clonedRequest := r.Clone(r.Context())
clonedRequest.Header.Set("Authorization", "Bearer "+b.Token)
clonedRequest.Header.Set("Accept", "application/json")

if b.Transport == nil {
b.Transport = http.DefaultTransport
}

return b.Transport.RoundTrip(clonedRequest)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Package gh_service exposes a simple service that makes it easy to get app installation tokens.
package gh_service
// Package ghservice exposes a simple service that makes it easy to get app installation tokens.
package ghservice

import (
"context"
Expand All @@ -9,7 +9,6 @@ import (
"net/http"
"time"

"github.com/bee-ci/bee-ci-system/internal/common/auth"
"github.com/golang-jwt/jwt/v5"
"github.com/google/go-github/v64/github"
)
Expand All @@ -30,20 +29,34 @@ func NewGithubService(githubAppID int64, rsaPrivateKey *rsa.PrivateKey) *GithubS
}
}

func (g GithubService) GetClientForInstallation(ctx context.Context, installationID int64) (*github.Client, error) {
// token, err := getFromRedis // TODO: Get from Redis

token, err := g.getInstallationAccessToken(ctx, installationID)
if err != nil {
return nil, fmt.Errorf("get installation access token: %w", err)
}

client := github.NewClient(&http.Client{
Transport: &bearerTransport{Token: token},
})

return client, nil
}

// TODO: Cache the token in some KV store, for example Redis. Before returning
// it, always check if 1 hour has passed.

// GetInstallationAccessToken returns the installation access token for the
// [installationID].
// getInstallationAccessToken returns the installation access token for the [installationID].
//
// The token returned is short-lived – per GitHub docs, it expires after 1 hour.
func (g GithubService) GetInstallationAccessToken(ctx context.Context, installationID int64) (string, error) {
func (g GithubService) getInstallationAccessToken(ctx context.Context, installationID int64) (string, error) {
jwtString, err := g.generateSignedJWT(g.githubAppID, g.rsaPrivateKey)
if err != nil {
return "", fmt.Errorf("generate signed jwt: %w", err)
}

appClient := http.Client{Transport: &auth.BearerTransport{Token: jwtString}}
appClient := http.Client{Transport: &bearerTransport{Token: jwtString}}
gh := github.NewClient(&appClient)
res, _, err := gh.Apps.CreateInstallationToken(ctx, installationID, nil)
if err != nil {
Expand All @@ -68,3 +81,21 @@ func (g GithubService) generateSignedJWT(githubAppID int64, rsaPrivateKey *rsa.P

return tokenStr, nil
}

type bearerTransport struct {
Token string
Transport http.RoundTripper
}

// RoundTrip implements http.RoundTripper interface.
func (b *bearerTransport) RoundTrip(r *http.Request) (*http.Response, error) {
clonedRequest := r.Clone(r.Context())
clonedRequest.Header.Set("Authorization", "Bearer "+b.Token)
clonedRequest.Header.Set("Accept", "application/json")

if b.Transport == nil {
b.Transport = http.DefaultTransport
}

return b.Transport.RoundTrip(clonedRequest)
}
21 changes: 8 additions & 13 deletions backend/internal/server/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import (
"strconv"
"time"

"github.com/bee-ci/bee-ci-system/internal/common/auth"
ghs "github.com/bee-ci/bee-ci-system/internal/common/gh_service"
ghs "github.com/bee-ci/bee-ci-system/internal/common/ghservice"

"github.com/golang-jwt/jwt/v5"
"github.com/google/go-github/v64/github"
Expand Down Expand Up @@ -254,17 +253,13 @@ func (h WebhookHandler) handleWebhook(w http.ResponseWriter, r *http.Request) {
if *event.Action == "created" {
// Example response: https://github.com/octokit/webhooks/blob/main/payload-examples/api.github.com/installation/created.payload.json

installationAccessToken, err := h.githubService.GetInstallationAccessToken(r.Context(), *installation.ID)
ghClient, err := h.githubService.GetClientForInstallation(r.Context(), *installation.ID)
if err != nil {
logger.Error("get installation access token", slog.Any("error", err))
logger.Error("get client for installation", slog.Any("error", err))
w.WriteHeader(http.StatusInternalServerError)
return
}

ghClient := github.NewClient(&http.Client{
Transport: &auth.BearerTransport{Token: installationAccessToken},
})

// The webhook event doesn't contain all repository data we need:
// - description
// - latest commit SHA
Expand Down Expand Up @@ -311,10 +306,10 @@ func (h WebhookHandler) handleWebhook(w http.ResponseWriter, r *http.Request) {
}
case "removed":
removedRepositories := event.RepositoriesRemoved
repos := mapRepos(userID, removedRepositories)
repoIDs := make([]int64, 0, len(repos))
for _, repo := range repos {
repoIDs = append(repoIDs, repo.ID)

repoIDs := make([]int64, 0, len(removedRepositories))
for _, removedRepository := range removedRepositories {
repoIDs = append(repoIDs, *removedRepository.ID)
}

err = h.repoRepo.Delete(r.Context(), repoIDs)
Expand All @@ -332,7 +327,7 @@ func (h WebhookHandler) handleWebhook(w http.ResponseWriter, r *http.Request) {

logger.Debug(fmt.Sprintf("check suite %s", *event.Action),
slog.String("owner", *event.Repo.Owner.Login),
slog.String("repo", *event.Repo.Name),
slog.String("removedRepository", *event.Repo.Name),
slog.Int64("installation_id", installationID),
slog.String("head_sha", headSHA),
)
Expand Down
20 changes: 5 additions & 15 deletions backend/internal/updater/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import (
"strconv"
"time"

ghs "github.com/bee-ci/bee-ci-system/internal/common/gh_service"

"github.com/bee-ci/bee-ci-system/internal/common/auth"
ghs "github.com/bee-ci/bee-ci-system/internal/common/ghservice"
"github.com/google/go-github/v64/github"

"github.com/bee-ci/bee-ci-system/internal/data"
Expand Down Expand Up @@ -130,15 +128,11 @@ func (u Updater) createCheckRun(ctx context.Context, build data.Build) (checkRun
return 0, fmt.Errorf("get user: %w", err)
}

installationAccessToken, err := u.githubService.GetInstallationAccessToken(ctx, build.InstallationID)
ghClient, err := u.githubService.GetClientForInstallation(ctx, build.InstallationID)
if err != nil {
return 0, fmt.Errorf("get installation access token: %w", err)
return 0, fmt.Errorf("get client for installation: %w", err)
}

ghClient := github.NewClient(&http.Client{
Transport: &auth.BearerTransport{Token: installationAccessToken},
})

createCheckRunOptions := github.CreateCheckRunOptions{
// TODO: Get name from the BeeCI config file?
Name: build.CommitMsg + ", started at: " + fmt.Sprint(time.Now().Format(time.RFC822Z)),
Expand Down Expand Up @@ -185,15 +179,11 @@ func (u Updater) updateCheckRun(ctx context.Context, checkRunID int64, build dat
return fmt.Errorf("get user: %w", err)
}

installationAccessToken, err := u.githubService.GetInstallationAccessToken(ctx, build.InstallationID)
ghClient, err := u.githubService.GetClientForInstallation(ctx, build.InstallationID)
if err != nil {
return fmt.Errorf("get installation access token: %w", err)
return fmt.Errorf("get client for installation: %w", err)
}

ghClient := github.NewClient(&http.Client{
Transport: &auth.BearerTransport{Token: installationAccessToken},
})

// TODO: Do I need to set these options again, or if I set them to null they will be removed?
checkRunUpdateOptions := github.UpdateCheckRunOptions{
Name: build.CommitMsg + ", started at: " + fmt.Sprint(time.Now().Format(time.RFC822Z)),
Expand Down

0 comments on commit dc94c45

Please sign in to comment.