Skip to content

Commit

Permalink
Don't iterate all GitHub repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Dec 11, 2023
1 parent 5a9ac20 commit c307dd6
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 4 deletions.
2 changes: 1 addition & 1 deletion controllers/component_dependency_update_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ func GetComponentFromPipelineRun(c client.Client, ctx context.Context, pipelineR
func DefaultDependenciesUpdate(ctx context.Context, client client.Client, scheme *runtime.Scheme, eventRecorder record.EventRecorder, downstreamComponents []applicationapi.Component, result *BuildResult) (immediateRetry bool, err error) {
log := ctrllog.FromContext(ctx)
log.Info("reading github installations")
slug, installationsToUpdate, err := GetGithubInstallations(ctx, client, eventRecorder, downstreamComponents)
slug, installationsToUpdate, err := GetGithubInstallationsForComponents(ctx, client, eventRecorder, downstreamComponents)
if err != nil || slug == "" {
return false, err
}
Expand Down
85 changes: 85 additions & 0 deletions controllers/renovate_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"os"
"regexp"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
log2 "sigs.k8s.io/controller-runtime/pkg/log"
Expand Down Expand Up @@ -99,6 +100,90 @@ func GetGithubInstallations(ctx context.Context, client client.Client, eventReco
return slug, installationsToUpdate, nil
}

func GetGithubInstallationsForComponents(ctx context.Context, client client.Client, eventRecorder record.EventRecorder, componentList []v1alpha1.Component) (string, []installationStruct, error) {
log := log2.FromContext(ctx)
// Check if GitHub Application is used, if not then skip
pacSecret := v1.Secret{}
globalPaCSecretKey := types.NamespacedName{Namespace: buildServiceNamespaceName, Name: prepare.PipelinesAsCodeSecretName}
if err := client.Get(ctx, globalPaCSecretKey, &pacSecret); err != nil {
eventRecorder.Event(&pacSecret, "Warning", "ErrorReadingPaCSecret", err.Error())
if errors.IsNotFound(err) {
log.Error(err, "not found Pipelines as Code secret in %s namespace: %w", globalPaCSecretKey.Namespace, err, logs.Action, logs.ActionView)
} else {
log.Error(err, "failed to get Pipelines as Code secret in %s namespace: %w", globalPaCSecretKey.Namespace, err, logs.Action, logs.ActionView)
}
return "", nil, nil
}
isApp := gitops.IsPaCApplicationConfigured("github", pacSecret.Data)
if !isApp {
log.Info("GitHub App is not set")
return "", nil, nil
}

// Load GitHub App and get GitHub Installations
githubAppIdStr := string(pacSecret.Data[gitops.PipelinesAsCode_githubAppIdKey])
privateKey := pacSecret.Data[gitops.PipelinesAsCode_githubPrivateKey]

ghUrlRegex, err := regexp.Compile("github.com/([^/])/([^/])(\\.git)?$")
if err != nil {
return "", nil, err
}

// Match installed repositories with Components and get custom branch if defined
installationsToUpdate := []installationStruct{}
var slug string
for _, component := range componentList {
if component.Spec.Source.GitSource == nil {
continue
}

gitSource := component.Spec.Source.GitSource
match := ghUrlRegex.FindStringSubmatch(gitSource.URL)
if match == nil {
log.Info(fmt.Sprintf("Unable to nudge %s as it is not a github repo", gitSource))
continue
}

githubAppInstallation, slugTmp, err := github.GetAppInstallationsForRepository(githubAppIdStr, privateKey, match[1], match[2])
if slug == "" {
slug = slugTmp
}
if err != nil {
log.Error(err, fmt.Sprintf("Failed to get GitHub app installation for component %s/%s", component.Namespace, component.Name))
continue
}

branch := gitSource.Revision
if branch == "" {
branch = InternalDefaultBranch
}

repositories := []renovateRepository{}
for _, repository := range githubAppInstallation.Repositories {
if branch == InternalDefaultBranch {
branch = repository.GetDefaultBranch()
}

repositories = append(repositories, renovateRepository{
BaseBranches: []string{branch},
Repository: repository.GetFullName(),
})
}
// Do not add intatallation which has no matching repositories
if len(repositories) == 0 {
continue
}
installationsToUpdate = append(installationsToUpdate,
installationStruct{
id: int(githubAppInstallation.ID),
token: githubAppInstallation.Token,
repositories: repositories,
})
}

return slug, installationsToUpdate, nil
}

func CreateRenovaterJob(ctx context.Context, client client.Client, scheme *runtime.Scheme, installations []installationStruct, slug string, debug bool, js func(slug string, repositories []renovateRepository, info interface{}) (string, error), info interface{}) error {
log := log2.FromContext(ctx)

Expand Down
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,8 @@ require (
k8s.io/klog v1.0.0 // indirect
k8s.io/kube-openapi v0.0.0-20231113174909-778a5567bc1e // indirect
k8s.io/pod-security-admission v0.26.1 // indirect
<<<<<<< HEAD
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
knative.dev/pkg v0.0.0-20230320014357-4c84b1b51ee8 // indirect
=======
>>>>>>> 263d9ea (Add renovate job)
oras.land/oras-go v1.1.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
Expand Down
52 changes: 52 additions & 0 deletions pkg/git/github/github_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var NewGithubClientForSimpleBuildByApp func(appId int64, privateKeyPem []byte) (

var IsAppInstalledIntoRepository func(ghclient *GithubClient, repoUrl string) (bool, error) = isAppInstalledIntoRepository
var GetAppInstallations func(githubAppIdStr string, appPrivateKeyPem []byte) ([]ApplicationInstallation, string, error) = getAppInstallations
var GetAppInstallationsForRepository func(githubAppIdStr string, appPrivateKeyPem []byte, owner string, repo string) (*ApplicationInstallation, string, error) = getAppInstallationsForRepository

func newGithubClientByApp(appId int64, privateKeyPem []byte, repoUrl string) (*GithubClient, error) {
owner, _ := getOwnerAndRepoFromUrl(repoUrl)
Expand Down Expand Up @@ -270,6 +271,57 @@ func getAppInstallations(githubAppIdStr string, appPrivateKeyPem []byte) ([]Appl
return appInstallations, slug, nil
}

func getAppInstallationsForRepository(githubAppIdStr string, appPrivateKeyPem []byte, owner string, repo string) (*ApplicationInstallation, string, error) {
githubAppId, err := strconv.ParseInt(githubAppIdStr, 10, 64)
if err != nil {
return nil, "", boerrors.NewBuildOpError(boerrors.EGitHubAppMalformedId,
fmt.Errorf("failed to convert %s to int: %w", githubAppIdStr, err))
}

itr, err := ghinstallation.NewAppsTransport(http.DefaultTransport, githubAppId, appPrivateKeyPem)
if err != nil {
// Inability to create transport based on a private key indicates that the key is bad formatted
return nil, "", boerrors.NewBuildOpError(boerrors.EGitHubAppMalformedPrivateKey, err)
}
client := github.NewClient(&http.Client{Transport: itr})
githubApp, _, err := client.Apps.Get(context.Background(), "")
if err != nil {
return nil, "", fmt.Errorf("failed to load GitHub app metadata, %w", err)
}
slug := (githubApp.GetSlug())
val, resp, err := client.Apps.FindRepositoryInstallation(context.Background(), owner, repo)
if err != nil {
if resp != nil && resp.Response != nil && resp.Response.StatusCode != 0 {
switch resp.StatusCode {
case 401:
return nil, "", boerrors.NewBuildOpError(boerrors.EGitHubAppPrivateKeyNotMatched, err)
case 404:
return nil, "", boerrors.NewBuildOpError(boerrors.EGitHubAppDoesNotExist, err)
}
}
return nil, "", boerrors.NewBuildOpError(boerrors.ETransientError, err)
}
token, _, err := client.Apps.CreateInstallationToken(
context.Background(),
*val.ID,
&github.InstallationTokenOptions{})
if err != nil {
return nil, "", err
}
installationClient := NewGithubClient(token.GetToken())

repositories, err := getRepositoriesFromClient(installationClient)
if err != nil {
return nil, "", err
}
return &ApplicationInstallation{
Token: token.GetToken(),
ID: *val.ID,
Repositories: repositories,
}, slug, nil

}

func getRepositoriesFromClient(ghClient *GithubClient) ([]*github.Repository, error) {
opt := &github.ListOptions{PerPage: 100}
var repos []*github.Repository
Expand Down

0 comments on commit c307dd6

Please sign in to comment.