diff --git a/cmd/app/initilization.go b/cmd/app/initilization.go index 380dcd7a..23e671a5 100644 --- a/cmd/app/initilization.go +++ b/cmd/app/initilization.go @@ -95,7 +95,7 @@ func newRepositoryHost(host string, client *github.Client, httpClient *http.Clie if host == "github.com" { rawHost = "raw.githubusercontent.com" } - return githubhttpcache.NewGHC(host, client, httpClient, &osshim.OsShim{}, []string{host, rawHost}, localMappings, options) + return githubhttpcache.NewGHC(host, client, client.Repositories, client.Git, httpClient, &osshim.OsShim{}, []string{host, rawHost}, localMappings, options) } // NewReactor creates a Reactor from Options diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index 562034ae..6e460069 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -38,11 +38,6 @@ func processManifest(f nodeTransformation, node *Node, parent *Node, manifest *N } func loadManifestStructure(node *Node, parent *Node, manifest *Node, r resourcehandlers.Registry) error { - var ( - err error - content string - newManifest string - ) if node.Manifest == "" { return nil } @@ -50,7 +45,8 @@ func loadManifestStructure(node *Node, parent *Node, manifest *Node, r resourceh if err != nil { return err } - if newManifest, err = fs.ToAbsLink(manifest.Manifest, node.Manifest); err != nil { + newManifest, err := fs.ToAbsLink(manifest.Manifest, node.Manifest) + if err != nil { return fmt.Errorf("can't build manifest node %s absolute URL : %w ", node.Manifest, err) } node.Manifest = newManifest @@ -58,7 +54,8 @@ func loadManifestStructure(node *Node, parent *Node, manifest *Node, r resourceh if err != nil { return err } - if content, err = fs.ManifestFromURL(node.Manifest); err != nil { + content, err := fs.ManifestFromURL(node.Manifest) + if err != nil { return fmt.Errorf("can't get manifest file content : %w", err) } if err = yaml.Unmarshal([]byte(content), node); err != nil { diff --git a/pkg/osfakes/httpclient/httpclientfakes/fake_client.go b/pkg/osfakes/httpclient/httpclientfakes/fake_client.go index 526dc431..532de8ad 100644 --- a/pkg/osfakes/httpclient/httpclientfakes/fake_client.go +++ b/pkg/osfakes/httpclient/httpclientfakes/fake_client.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/osfakes/osshim/osshimfakes/fake_os.go b/pkg/osfakes/osshim/osshimfakes/fake_os.go index cf9cf12e..fd04c6b6 100644 --- a/pkg/osfakes/osshim/osshimfakes/fake_os.go +++ b/pkg/osfakes/osshim/osshimfakes/fake_os.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/readers/link/resource_link.go b/pkg/readers/link/resource_link.go index c8e7449f..3462410c 100644 --- a/pkg/readers/link/resource_link.go +++ b/pkg/readers/link/resource_link.go @@ -7,7 +7,6 @@ package link import ( "fmt" "net/url" - "path" "regexp" ) @@ -100,13 +99,3 @@ func (r *Resource) GetRepoURL() string { func (r *Resource) GetRawURL() string { return fmt.Sprintf("https://%s/%s/%s/raw/%s/%s", r.Host, r.Owner, r.Repo, r.Ref, r.Path) } - -// GetResourceName returns the name of the resource (including extension), if resource path is empty returns '.' -func (r *Resource) GetResourceName() string { - return path.Base(r.Path) -} - -// GetResourceExt returns the resource name extension, empty string if when no extension exists -func (r *Resource) GetResourceExt() string { - return path.Ext(r.Path) -} diff --git a/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache.go b/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache.go index 44d30d06..3b8d7560 100644 --- a/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache.go +++ b/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache.go @@ -4,6 +4,8 @@ package githubhttpcache +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate -header ../../../../license_prefix.txt + import ( "context" "encoding/base64" @@ -15,6 +17,7 @@ import ( "os" "path" "path/filepath" + "slices" "sort" "strings" "sync" @@ -32,8 +35,10 @@ import ( // GHC implements repositoryhosts.RepositoryHost interface using GitHub manifestadapter with transport level persistent cache. type GHC struct { hostName string - client *github.Client - httpClient *http.Client + client httpclient.Client + git Git + rateLimit RateLimitSource + repositories Repositories os osshim.Os acceptedHosts []string localMappings map[string]string @@ -45,12 +50,38 @@ type GHC struct { options manifest.ParsingOptions } +//counterfeiter:generate . RateLimitSource + +// RateLimitSource is an interface needed for faking +type RateLimitSource interface { + RateLimits(ctx context.Context) (*github.RateLimits, *github.Response, error) +} + +//counterfeiter:generate . Repositories + +// Repositories is an interface needed for faking +type Repositories interface { + ListCommits(ctx context.Context, owner, repo string, opts *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error) + GetContents(ctx context.Context, owner, repo, path string, opts *github.RepositoryContentGetOptions) (fileContent *github.RepositoryContent, directoryContent []*github.RepositoryContent, resp *github.Response, err error) + Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error) +} + +//counterfeiter:generate . Git + +// Git is an interface needed for faking +type Git interface { + GetBlobRaw(ctx context.Context, owner, repo, sha string) ([]byte, *github.Response, error) + GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*github.Tree, *github.Response, error) +} + // NewGHC creates new GHC resource handler -func NewGHC(hostName string, client *github.Client, httpClient *http.Client, os osshim.Os, acceptedHosts []string, localMappings map[string]string, options manifest.ParsingOptions) repositoryhosts.RepositoryHost { +func NewGHC(hostName string, rateLimit RateLimitSource, repositories Repositories, git Git, client httpclient.Client, os osshim.Os, acceptedHosts []string, localMappings map[string]string, options manifest.ParsingOptions) repositoryhosts.RepositoryHost { return &GHC{ hostName: hostName, client: client, - httpClient: httpClient, + git: git, + rateLimit: rateLimit, + repositories: repositories, os: os, acceptedHosts: acceptedHosts, localMappings: localMappings, @@ -80,8 +111,8 @@ type GitInfo struct { //========================= manifest.FileSource =================================================== // FileTreeFromURL implements manifest.FileSource#FileTreeFromURL -func (p *GHC) FileTreeFromURL(url string) ([]string, error) { - r, err := p.getResolvedResourceInfo(context.TODO(), url) +func (p *GHC) FileTreeFromURL(URL string) ([]string, error) { + r, err := p.getResolvedResourceInfo(context.TODO(), URL) if err != nil { return nil, err } @@ -94,12 +125,20 @@ func (p *GHC) FileTreeFromURL(url string) ([]string, error) { if local := p.checkForLocalMapping(r); len(local) > 0 { return p.readLocalFileTree(*r, local), nil } - t, err := p.getTree(context.TODO(), r, true) + sha := fmt.Sprintf("%s:%s", r.Ref, r.Path) + sha = url.PathEscape(sha) + tree, resp, err := p.git.GetTree(context.TODO(), r.Owner, r.Repo, sha, true) + if resp != nil && resp.StatusCode == http.StatusNotFound { + return nil, repositoryhosts.ErrResourceNotFound(r.String()) + } + if resp != nil && resp.StatusCode >= 400 { + return nil, fmt.Errorf("reading tree %s fails with HTTP status: %d", r.String(), resp.StatusCode) + } if err != nil { return nil, err } res := []string{} - for _, e := range t.Entries { + for _, e := range tree.Entries { extracted := false ePath := strings.TrimPrefix(*e.Path, "/") for _, extractedFormat := range p.options.ExtractedFilesFormats { @@ -114,7 +153,6 @@ func (p *GHC) FileTreeFromURL(url string) ([]string, error) { continue } res = append(res, ePath) - } return res, nil } @@ -125,13 +163,12 @@ func (p *GHC) ManifestFromURL(url string) (string, error) { if err != nil { return "", err } - content, err := p.Read(context.TODO(), r.GetResourceURL()) + content, err := p.Read(context.TODO(), r.String()) return string(content), err } // ToAbsLink implements manifest.FileSource#ToAbsLink func (p *GHC) ToAbsLink(source, link string) (string, error) { - r, err := p.getResolvedResourceInfo(context.TODO(), source) if err != nil { return link, err @@ -141,11 +178,40 @@ func (p *GHC) ToAbsLink(source, link string) (string, error) { if err != nil { return link, err } - link = l.GetResourceURL() + link = l.String() + } + l, err := url.Parse(strings.TrimSuffix(link, "/")) + if err != nil { + return link, err + } + if l.IsAbs() { + return link, nil // already absolute } - res, err := p.buildAbsLink(r, link) + // build URL based on source path + u, err := url.Parse("/" + r.Path) + if err != nil { + return link, err + } + if u, err = u.Parse(l.Path); err != nil { + return link, err + } + // determine the type of the resource: (blob|tree) + var tp string + if tp, err = p.determineLinkType(r, u); err != nil { + return tp, err + } + res, err := url.Parse(r.URL.String()) + if err != nil { + return "", err + } + // set path + res.Path = fmt.Sprintf("/%s/%s/%s/%s%s", r.Owner, r.Repo, tp, r.Ref, u.Path) + // set query & fragment + res.ForceQuery = l.ForceQuery + res.RawQuery = l.RawQuery + res.Fragment = l.Fragment - return res, err + return res.String(), nil } //========================= repositoryhosts.RepositoryHost =================================================== @@ -157,12 +223,12 @@ func (p *GHC) Name() string { // Accept implements the repositoryhosts.RepositoryHost#Accept func (p *GHC) Accept(uri string) bool { - r, err := link.NewResource(uri) - if err != nil || r.URL.Scheme != "https" { + r, err := url.Parse(uri) + if err != nil || r.Scheme != "https" { return false } for _, h := range p.acceptedHosts { - if h == r.URL.Host { + if h == r.Host { return true } } @@ -181,7 +247,46 @@ func (p *GHC) Read(ctx context.Context, uri string) ([]byte, error) { if local := p.checkForLocalMapping(r); len(local) > 0 { return p.readLocalFile(ctx, r, local) } - return p.readFile(ctx, r) + // read using GitService and file URL -> file SHA mapping + if SHA, ok := p.getFileSHA(r.String()); ok { + raw, resp, err := p.git.GetBlobRaw(ctx, r.Owner, r.Repo, SHA) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + return nil, repositoryhosts.ErrResourceNotFound(r.String()) + } + return nil, err + } + if resp != nil && resp.StatusCode >= 400 { + return nil, fmt.Errorf("reading blob %s fails with HTTP status: %d", r.String(), resp.StatusCode) + } + return raw, nil + } + // read using RepositoriesService.DownloadContents for non-markdown and non-manifest files - 2 manifestadapter calls + opt := &github.RepositoryContentGetOptions{Ref: r.Ref} + if !strings.HasSuffix(strings.ToLower(r.Path), ".md") && !strings.HasSuffix(strings.ToLower(r.Path), ".yaml") { + return p.downloadContent(ctx, opt, r) + } + // read using RepositoriesService.GetContents for markdowns and module manifests - 1 manifestadapter call + fc, _, resp, err := p.repositories.GetContents(ctx, r.Owner, r.Repo, r.Path, opt) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + return nil, repositoryhosts.ErrResourceNotFound(r.String()) + } + if resp != nil && resp.StatusCode == http.StatusForbidden { + // if file is bigger than 1 MB -> content should be downloaded + // it makes two additional manifestadapter cals, but it's unlikely to have large manifest.yaml + return p.downloadContent(ctx, opt, r) + } + return nil, err + } + if resp != nil && resp.StatusCode >= 400 { + return nil, fmt.Errorf("reading blob %s fails with HTTP status: %d", r.String(), resp.StatusCode) + } + cnt, err := base64.StdEncoding.DecodeString(*fc.Content) + if err != nil { + return nil, err + } + return cnt, nil } // ReadGitInfo implements the repositoryhosts.RepositoryHost#ReadGitInfo @@ -196,30 +301,23 @@ func (p *GHC) ReadGitInfo(ctx context.Context, uri string) ([]byte, error) { } var commits []*github.RepositoryCommit var resp *github.Response - if commits, resp, err = p.client.Repositories.ListCommits(ctx, r.Owner, r.Repo, opts); err != nil { + if commits, resp, err = p.repositories.ListCommits(ctx, r.Owner, r.Repo, opts); err != nil { return nil, err } if resp != nil && resp.StatusCode >= 400 { return nil, fmt.Errorf("list commits for %s fails with HTTP status: %d", r.String(), resp.StatusCode) } - var blob []byte - if commits != nil { - gitInfo := transform(commits) - if gitInfo == nil { - return nil, nil - } - - if len(r.Ref) > 0 { - gitInfo.SHAAlias = &r.Ref - } - if len(r.Path) > 0 { - gitInfo.Path = &r.Path - } - if blob, err = marshallGitInfo(gitInfo); err != nil { - return nil, err - } + gitInfo := transform(commits) + if gitInfo == nil { + return nil, nil + } + if len(r.Ref) > 0 { + gitInfo.SHAAlias = &r.Ref } - return blob, nil + if len(r.Path) > 0 { + gitInfo.Path = &r.Path + } + return json.MarshalIndent(gitInfo, "", " ") } // GetRawFormatLink implements the repositoryhosts.RepositoryHost#GetRawFormatLink @@ -236,12 +334,12 @@ func (p *GHC) GetRawFormatLink(absLink string) (string, error) { // GetClient implements the repositoryhosts.RepositoryHost#GetClient func (p *GHC) GetClient() httpclient.Client { - return p.httpClient + return p.client } // GetRateLimit implements the repositoryhosts.RepositoryHost#GetRateLimit func (p *GHC) GetRateLimit(ctx context.Context) (int, int, time.Time, error) { - r, _, err := p.client.RateLimits(ctx) + r, _, err := p.rateLimit.RateLimits(ctx) if err != nil { return -1, -1, time.Now(), err } @@ -261,51 +359,6 @@ func (p *GHC) checkForLocalMapping(r *link.Resource) string { return p.localMappings[key+"/"] } -// readFile reads a file from GitHub -func (p *GHC) readFile(ctx context.Context, r *link.Resource) ([]byte, error) { - var cnt []byte - // read using GitService and file URL -> file SHA mapping - if SHA, ok := p.getFileSHA(r.String()); ok { - raw, resp, err := p.client.Git.GetBlobRaw(ctx, r.Owner, r.Repo, SHA) - if err != nil { - if resp != nil && resp.StatusCode == http.StatusNotFound { - return nil, repositoryhosts.ErrResourceNotFound(r.String()) - } - return nil, err - } - if resp != nil && resp.StatusCode >= 400 { - return nil, fmt.Errorf("reading blob %s fails with HTTP status: %d", r.String(), resp.StatusCode) - } - return raw, nil - } - // read using RepositoriesService.DownloadContents for non-markdown and non-manifest files - 2 manifestadapter calls - opt := &github.RepositoryContentGetOptions{Ref: r.Ref} - if !strings.HasSuffix(strings.ToLower(r.Path), ".md") && !strings.HasSuffix(strings.ToLower(r.Path), ".yaml") { - return p.downloadContent(ctx, opt, r) - } - // read using RepositoriesService.GetContents for markdowns and module manifests - 1 manifestadapter call - fc, _, resp, err := p.client.Repositories.GetContents(ctx, r.Owner, r.Repo, r.Path, opt) - if err != nil { - if resp != nil && resp.StatusCode == http.StatusNotFound { - return nil, repositoryhosts.ErrResourceNotFound(r.String()) - } - if resp != nil && resp.StatusCode == http.StatusForbidden { - // if file is bigger than 1 MB -> content should be downloaded - // it makes two additional manifestadapter cals, but it's unlikely to have large manifest.yaml - return p.downloadContent(ctx, opt, r) - } - return nil, err - } - if resp != nil && resp.StatusCode >= 400 { - return nil, fmt.Errorf("reading blob %s fails with HTTP status: %d", r.String(), resp.StatusCode) - } - cnt, err = base64.StdEncoding.DecodeString(*fc.Content) - if err != nil { - return nil, err - } - return cnt, nil -} - // readLocalFile reads a file from FS func (p *GHC) readLocalFile(_ context.Context, r *link.Resource, localPath string) ([]byte, error) { fn := filepath.Join(localPath, r.Path) @@ -347,8 +400,7 @@ func (p *GHC) downloadContent(ctx context.Context, opt *github.RepositoryContent if contents.SHA == nil || *contents.SHA == "" { return nil, fmt.Errorf("no SHA found for %s", r.String()) } - var cnt []byte - cnt, resp, err = p.client.Git.GetBlobRaw(ctx, r.Owner, r.Repo, *contents.SHA) + cnt, resp, err := p.git.GetBlobRaw(ctx, r.Owner, r.Repo, *contents.SHA) if err != nil { if resp != nil && resp.StatusCode == http.StatusNotFound { return nil, repositoryhosts.ErrResourceNotFound(r.String()) @@ -369,74 +421,16 @@ func (p *GHC) downloadContent(ctx context.Context, opt *github.RepositoryContent func (p *GHC) getDirContents(ctx context.Context, owner, repo, path string, opts *github.RepositoryContentGetOptions) (dc []*github.RepositoryContent, resp *github.Response, err error) { p.muxCnt.Lock() defer p.muxCnt.Unlock() - _, dc, resp, err = p.client.Repositories.GetContents(ctx, owner, repo, path, opts) + _, dc, resp, err = p.repositories.GetContents(ctx, owner, repo, path, opts) return } -// getTree returns subtree with root r#Path -func (p *GHC) getTree(ctx context.Context, r *link.Resource, recursive bool) (*github.Tree, error) { - sha := fmt.Sprintf("%s:%s", r.Ref, r.Path) - sha = url.PathEscape(sha) - gitTree, resp, err := p.client.Git.GetTree(ctx, r.Owner, r.Repo, sha, recursive) - if err != nil { - if resp != nil && resp.StatusCode == http.StatusNotFound { - return nil, repositoryhosts.ErrResourceNotFound(r.String()) - } - return nil, err - } - if resp != nil && resp.StatusCode >= 400 { - return nil, fmt.Errorf("reading tree %s fails with HTTP status: %d", r.String(), resp.StatusCode) - } - return gitTree, nil -} - -// buildAbsLink builds absolute link if is relative using as a base -// repositoryhosts.ErrResourceNotFound if target resource doesn't exist -func (p *GHC) buildAbsLink(source *link.Resource, link string) (string, error) { - l, err := url.Parse(strings.TrimSuffix(link, "/")) - if err != nil { - return link, err - } - if l.IsAbs() { - return link, nil // already absolute - } - // build URL based on source path - var u *url.URL - - if u, err = url.Parse("/" + source.Path); err != nil { - return link, err - } - if u, err = u.Parse(l.Path); err != nil { - return link, err - } - if link == "testedDir/testedMarkdownFile3.md" { - fmt.Println("Why this bullshit " + source.Path) - } - // determine the type of the resource: (blob|tree) - var tp string - if tp, err = p.determineLinkType(source, u); err != nil { - return tp, err - } - res, err := url.Parse(source.URL.String()) - if err != nil { - return "", err - } - // set path - res.Path = fmt.Sprintf("/%s/%s/%s/%s%s", source.Owner, source.Repo, tp, source.Ref, u.Path) - // set query & fragment - res.ForceQuery = l.ForceQuery - res.RawQuery = l.RawQuery - res.Fragment = l.Fragment - - return res.String(), nil -} - // determineLinkType returns the type of relative link (blob|tree) // repositoryhosts.ErrResourceNotFound if target resource doesn't exist func (p *GHC) determineLinkType(source *link.Resource, rel *url.URL) (string, error) { var tp string var err error - gtp := "tree" // guess the type of resource + gtp := "tree" if len(path.Ext(rel.Path)) > 0 { gtp = "blob" } @@ -504,13 +498,14 @@ func (p *GHC) getResolvedResourceInfo(ctx context.Context, uri string) (*link.Re if err != nil { return nil, err } - if r.Ref == "DEFAULT_BRANCH" { - defaultBranch, err := p.getDefaultBranch(ctx, r.Owner, r.Repo) - if err != nil { - return nil, err - } - r.Ref = defaultBranch + if r.Ref != "DEFAULT_BRANCH" { + return &r, nil + } + defaultBranch, err := p.getDefaultBranch(ctx, r.Owner, r.Repo) + if err != nil { + return nil, err } + r.Ref = defaultBranch return &r, nil } @@ -522,7 +517,7 @@ func (p *GHC) getDefaultBranch(ctx context.Context, owner string, repository str if def, ok := p.defBranches[key]; ok { return def, nil } - repo, _, err := p.client.Repositories.Get(ctx, owner, repository) + repo, _, err := p.repositories.Get(ctx, owner, repository) if err != nil { return "", err } @@ -544,66 +539,41 @@ func transform(commits []*github.RepositoryCommit) *GitInfo { return nil } gitInfo := &GitInfo{} - var nonInternalCommits []*github.RepositoryCommit // skip internal commits - for _, commit := range commits { - if !isInternalCommit(commit) { - nonInternalCommits = append(nonInternalCommits, commit) - } - } + nonInternalCommits := slices.DeleteFunc(commits, isInternalCommit) if len(nonInternalCommits) == 0 { return nil } sort.Slice(nonInternalCommits, func(i, j int) bool { return nonInternalCommits[i].GetCommit().GetCommitter().GetDate().After(nonInternalCommits[j].GetCommit().GetCommitter().GetDate()) }) - lastModifiedDate := nonInternalCommits[0].GetCommit().GetCommitter().GetDate().Format(DateFormat) gitInfo.LastModifiedDate = &lastModifiedDate + webURL := nonInternalCommits[0].GetHTMLURL() - webURL = strings.Split(webURL, "/commit/")[0] - gitInfo.WebURL = &webURL + gitInfo.WebURL = github.String(strings.Split(webURL, "/commit/")[0]) - publishDate := commits[len(nonInternalCommits)-1].GetCommit().GetCommitter().GetDate().Format(DateFormat) - gitInfo.PublishDate = &publishDate + gitInfo.PublishDate = github.String(nonInternalCommits[len(nonInternalCommits)-1].GetCommit().GetCommitter().GetDate().Format(DateFormat)) if gitInfo.Author = getCommitAuthor(nonInternalCommits[len(nonInternalCommits)-1]); gitInfo.Author == nil { klog.Warningf("cannot get commit author") } - if len(nonInternalCommits) > 1 { - gitInfo.Contributors = []*github.User{} - var registered []string - for _, commit := range nonInternalCommits { - var contributor *github.User - if contributor = getCommitAuthor(commit); contributor == nil { - continue - } - if contributor.GetType() == "User" && contributor.GetEmail() != gitInfo.Author.GetEmail() && !contains(registered, contributor.GetEmail()) { - gitInfo.Contributors = append(gitInfo.Contributors, contributor) - registered = append(registered, contributor.GetEmail()) - } - } + if len(nonInternalCommits) < 2 { + return gitInfo } - - return gitInfo -} - -func contains(slice []string, s string) bool { - for _, _s := range slice { - if s == _s { - return true + gitInfo.Contributors = []*github.User{} + var registered []string + for _, commit := range nonInternalCommits { + var contributor *github.User + if contributor = getCommitAuthor(commit); contributor == nil { + continue + } + if contributor.GetType() == "User" && contributor.GetEmail() != gitInfo.Author.GetEmail() && slices.Index(registered, contributor.GetEmail()) < 0 { + gitInfo.Contributors = append(gitInfo.Contributors, contributor) + registered = append(registered, contributor.GetEmail()) } } - return false -} - -// marshallGitInfo serializes git.Info to byte array -func marshallGitInfo(gitInfo *GitInfo) ([]byte, error) { - blob, err := json.MarshalIndent(gitInfo, "", " ") - if err != nil { - return nil, err - } - return blob, nil + return gitInfo } func isInternalCommit(commit *github.RepositoryCommit) bool { @@ -615,27 +585,20 @@ func isInternalCommit(commit *github.RepositoryCommit) bool { strings.HasPrefix(email, "gardener.opensource") } -func mergeAuthors(author *github.User, commitAuthor *github.CommitAuthor) *github.User { - if author == nil { - author = &github.User{} - } - if commitAuthor != nil { - author.Name = commitAuthor.Name - author.Email = commitAuthor.Email - } - return author -} - func getCommitAuthor(commit *github.RepositoryCommit) *github.User { - var contributor *github.User - if contributor = commit.GetAuthor(); contributor != nil && commit.GetCommit().GetAuthor() != nil { - contributor = mergeAuthors(contributor, commit.GetCommit().GetAuthor()) + getCommitAuthor := commit.GetCommit().GetAuthor() + getCommitCommiter := commit.GetCommit().GetCommitter() + contributor := commit.GetAuthor() + if contributor != nil && getCommitAuthor != nil { + contributor.Name = getCommitAuthor.Name + contributor.Email = getCommitAuthor.Email + return contributor } - if contributor == nil && commit.GetCommit().GetAuthor() != nil { - contributor = mergeAuthors(&github.User{}, commit.GetCommit().GetAuthor()) + if getCommitAuthor != nil { + return &github.User{Name: getCommitAuthor.Name, Email: getCommitAuthor.Email} } - if contributor == nil && commit.GetCommit().GetCommitter() != nil { - contributor = mergeAuthors(&github.User{}, commit.GetCommit().GetCommitter()) + if getCommitCommiter != nil { + return &github.User{Name: getCommitCommiter.Name, Email: getCommitCommiter.Email} } - return contributor + return nil } diff --git a/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache_test.go b/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache_test.go new file mode 100644 index 00000000..f88d0d6e --- /dev/null +++ b/pkg/readers/repositoryhosts/githubhttpcache/github_http_cache_test.go @@ -0,0 +1,267 @@ +package githubhttpcache_test + +// SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +import ( + "context" + "encoding/base64" + "errors" + "net/http" + "testing" + "time" + + "github.com/gardener/docforge/pkg/manifest" + "github.com/gardener/docforge/pkg/osfakes/httpclient" + "github.com/gardener/docforge/pkg/osfakes/osshim" + "github.com/gardener/docforge/pkg/readers/repositoryhosts" + "github.com/gardener/docforge/pkg/readers/repositoryhosts/githubhttpcache" + "github.com/gardener/docforge/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes" + "github.com/google/go-github/v43/github" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestGithubCache(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Github cache Suite") +} + +var _ = Describe("Github cache test", func() { + var ( + ghc repositoryhosts.RepositoryHost + rls githubhttpcachefakes.FakeRateLimitSource + repositories githubhttpcachefakes.FakeRepositories + git githubhttpcachefakes.FakeGit + client httpclient.Client + os osshim.Os + ) + + BeforeEach(func() { + rls = githubhttpcachefakes.FakeRateLimitSource{} + repositories = githubhttpcachefakes.FakeRepositories{} + git = githubhttpcachefakes.FakeGit{} + }) + + JustBeforeEach(func() { + ghc = githubhttpcache.NewGHC("testing", &rls, &repositories, &git, client, os, []string{"github.com"}, map[string]string{}, manifest.ParsingOptions{ExtractedFilesFormats: []string{".md"}, Hugo: true}) + }) + + Describe("#GetRateLimit", func() { + BeforeEach(func() { + rls.RateLimitsReturns(nil, nil, errors.New("yataa error")) + }) + + It("return correct rate limit", func() { + _, _, _, err := ghc.GetRateLimit(context.TODO()) + Expect(err).To(Equal(errors.New("yataa error"))) + + }) + }) + + Describe("#FileTreeFromURL", func() { + It("not a tree url", func() { + _, err := ghc.FileTreeFromURL("https://github.com/gardener/docforge/blob/master/README.md") + Expect(err.Error()).To(ContainSubstring("not a tree url")) + }) + + Describe("not found", func() { + BeforeEach(func() { + resp := github.Response{Response: &http.Response{StatusCode: http.StatusNotFound}} + git.GetTreeReturns(nil, &resp, nil) + }) + + It("not found", func() { + _, err := ghc.FileTreeFromURL("https://github.com/gardener/docforge/tree/master/pkg") + Expect(err.Error()).To(ContainSubstring("not found")) + }) + }) + + Describe("reading tree fails", func() { + BeforeEach(func() { + resp := github.Response{Response: &http.Response{StatusCode: 503}} + git.GetTreeReturns(nil, &resp, nil) + }) + + It("not found", func() { + _, err := ghc.FileTreeFromURL("https://github.com/gardener/docforge/tree/master/pkg") + Expect(err.Error()).To(ContainSubstring("fails with HTTP status: 503")) + }) + }) + + Describe("reading tree succeeds", func() { + BeforeEach(func() { + tree := github.Tree{ + Entries: []*github.TreeEntry{ + { + Path: github.String("/README.md"), + Type: github.String("blob"), + }, + { + Path: github.String("/Makefile"), + Type: github.String("blob"), + }, + { + Path: github.String("/pkg"), + Type: github.String("tree"), + }, + { + Path: github.String("/pkg/main.go"), + Type: github.String("blob"), + }, + { + Path: github.String("/docs/_index.md"), + Type: github.String("blob"), + }, + }, + } + git.GetTreeReturns(&tree, nil, nil) + }) + + It("not found", func() { + tree, err := ghc.FileTreeFromURL("https://github.com/gardener/docforge/tree/master/pkg") + Expect(tree).To(Equal([]string{"README.md", "docs/_index.md"})) + Expect(err).NotTo(HaveOccurred()) + + }) + }) + + }) + + Describe("#ToAbsLink", func() { + Describe("absolute link", func() { + It("returns unmodified abs link", func() { + url, err := ghc.ToAbsLink("https://github.com/gardener/docforge/blob/master/README.md", "https://github.com/gardener/docforge/raw/master/docs/one.png") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/gardener/docforge/raw/master/docs/one.png")) + }) + }) + + Describe("relative path", func() { + BeforeEach(func() { + docsContent := []*github.RepositoryContent{ + { + Name: github.String("one.md"), + Type: github.String("file"), + HTMLURL: github.String("https://github.com/gardener/docforge/blob/master/docs/one.md"), + SHA: github.String("123"), + }, + { + Name: github.String("developer"), + Type: github.String("directory"), + HTMLURL: github.String("https://github.com/gardener/docforge/tree/master/docs/developer"), + SHA: github.String("234"), + }, + } + repositories.GetContentsReturns(nil, docsContent, nil, nil) + }) + + It("returns correct abs link of a file", func() { + url, err := ghc.ToAbsLink("https://github.com/gardener/docforge/blob/master/README.md", "../docs/one.md") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/gardener/docforge/blob/master/docs/one.md")) + }) + + It("returns correct abs link of a directory", func() { + url, err := ghc.ToAbsLink("https://github.com/gardener/docforge/blob/master/README.md", "../docs/developer") + Expect(err).NotTo(HaveOccurred()) + Expect(url).To(Equal("https://github.com/gardener/docforge/tree/master/docs/developer")) + + }) + }) + }) + + Describe("#Read", func() { + Describe("md file", func() { + BeforeEach(func() { + byteContent := []byte("foo") + docContent := &github.RepositoryContent{ + Content: github.String(base64.StdEncoding.EncodeToString(byteContent)), + } + repositories.GetContentsReturns(docContent, nil, nil, nil) + }) + + It("returns correct content", func() { + content, err := ghc.Read(context.TODO(), "https://github.com/gardener/docforge/blob/master/README.md") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(Equal("foo")) + }) + }) + Describe("png file", func() { + BeforeEach(func() { + docsContent := []*github.RepositoryContent{ + { + Name: github.String("logo.png"), + SHA: github.String("321"), + }, + } + repositories.GetContentsReturns(nil, docsContent, nil, nil) + git.GetBlobRawReturns([]byte("logo_contents"), nil, nil) + }) + + It("returns correct content", func() { + content, err := ghc.Read(context.TODO(), "https://github.com/gardener/docforge/blob/master/logo.png") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(Equal("logo_contents")) + }) + }) + }) + + Describe("#ManifestFromURL", func() { + BeforeEach(func() { + byteContent := []byte("foo") + docContent := &github.RepositoryContent{ + Content: github.String(base64.StdEncoding.EncodeToString(byteContent)), + } + repositories.GetContentsReturns(docContent, nil, nil, nil) + }) + + It("returns correct content", func() { + content, err := ghc.ManifestFromURL("https://github.com/gardener/docforge/blob/master/manifest.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(Equal("foo")) + }) + + }) + + Describe("#ReadGitInfo", func() { + BeforeEach(func() { + time1 := time.Date(2024, time.February, 6, 13, 11, 0, 0, time.UTC) + time2 := time.Date(2024, time.February, 7, 13, 11, 0, 0, time.UTC) + commits := []*github.RepositoryCommit{ + { + Author: &github.User{ + Name: github.String("one"), + Email: github.String("one@"), + Type: github.String("User"), + }, + Commit: &github.Commit{ + Committer: &github.CommitAuthor{ + Date: &time1, + Name: github.String("one"), + Email: github.String("one@"), + }, + }, + HTMLURL: github.String("foo"), + }, + { + Commit: &github.Commit{ + Committer: &github.CommitAuthor{ + Date: &time2, + }, + }, + HTMLURL: github.String("bar"), + }, + } + repositories.ListCommitsReturns(commits, nil, nil) + }) + + It("returns correct git info", func() { + content, err := ghc.ReadGitInfo(context.TODO(), "https://github.com/gardener/docforge/blob/master/README.md") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(Equal("{\n \"lastmod\": \"2024-02-07 13:11:00\",\n \"publishdate\": \"2024-02-06 13:11:00\",\n \"author\": {\n \"name\": \"one\",\n \"email\": \"one@\"\n },\n \"weburl\": \"bar\",\n \"shaalias\": \"master\",\n \"path\": \"README.md\"\n}")) + }) + }) + +}) diff --git a/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_git.go b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_git.go new file mode 100644 index 00000000..b97a0862 --- /dev/null +++ b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_git.go @@ -0,0 +1,224 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +// Code generated by counterfeiter. DO NOT EDIT. +package githubhttpcachefakes + +import ( + "context" + "sync" + + "github.com/gardener/docforge/pkg/readers/repositoryhosts/githubhttpcache" + "github.com/google/go-github/v43/github" +) + +type FakeGit struct { + GetBlobRawStub func(context.Context, string, string, string) ([]byte, *github.Response, error) + getBlobRawMutex sync.RWMutex + getBlobRawArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + } + getBlobRawReturns struct { + result1 []byte + result2 *github.Response + result3 error + } + getBlobRawReturnsOnCall map[int]struct { + result1 []byte + result2 *github.Response + result3 error + } + GetTreeStub func(context.Context, string, string, string, bool) (*github.Tree, *github.Response, error) + getTreeMutex sync.RWMutex + getTreeArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + arg5 bool + } + getTreeReturns struct { + result1 *github.Tree + result2 *github.Response + result3 error + } + getTreeReturnsOnCall map[int]struct { + result1 *github.Tree + result2 *github.Response + result3 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeGit) GetBlobRaw(arg1 context.Context, arg2 string, arg3 string, arg4 string) ([]byte, *github.Response, error) { + fake.getBlobRawMutex.Lock() + ret, specificReturn := fake.getBlobRawReturnsOnCall[len(fake.getBlobRawArgsForCall)] + fake.getBlobRawArgsForCall = append(fake.getBlobRawArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + }{arg1, arg2, arg3, arg4}) + stub := fake.GetBlobRawStub + fakeReturns := fake.getBlobRawReturns + fake.recordInvocation("GetBlobRaw", []interface{}{arg1, arg2, arg3, arg4}) + fake.getBlobRawMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeGit) GetBlobRawCallCount() int { + fake.getBlobRawMutex.RLock() + defer fake.getBlobRawMutex.RUnlock() + return len(fake.getBlobRawArgsForCall) +} + +func (fake *FakeGit) GetBlobRawCalls(stub func(context.Context, string, string, string) ([]byte, *github.Response, error)) { + fake.getBlobRawMutex.Lock() + defer fake.getBlobRawMutex.Unlock() + fake.GetBlobRawStub = stub +} + +func (fake *FakeGit) GetBlobRawArgsForCall(i int) (context.Context, string, string, string) { + fake.getBlobRawMutex.RLock() + defer fake.getBlobRawMutex.RUnlock() + argsForCall := fake.getBlobRawArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 +} + +func (fake *FakeGit) GetBlobRawReturns(result1 []byte, result2 *github.Response, result3 error) { + fake.getBlobRawMutex.Lock() + defer fake.getBlobRawMutex.Unlock() + fake.GetBlobRawStub = nil + fake.getBlobRawReturns = struct { + result1 []byte + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeGit) GetBlobRawReturnsOnCall(i int, result1 []byte, result2 *github.Response, result3 error) { + fake.getBlobRawMutex.Lock() + defer fake.getBlobRawMutex.Unlock() + fake.GetBlobRawStub = nil + if fake.getBlobRawReturnsOnCall == nil { + fake.getBlobRawReturnsOnCall = make(map[int]struct { + result1 []byte + result2 *github.Response + result3 error + }) + } + fake.getBlobRawReturnsOnCall[i] = struct { + result1 []byte + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeGit) GetTree(arg1 context.Context, arg2 string, arg3 string, arg4 string, arg5 bool) (*github.Tree, *github.Response, error) { + fake.getTreeMutex.Lock() + ret, specificReturn := fake.getTreeReturnsOnCall[len(fake.getTreeArgsForCall)] + fake.getTreeArgsForCall = append(fake.getTreeArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + arg5 bool + }{arg1, arg2, arg3, arg4, arg5}) + stub := fake.GetTreeStub + fakeReturns := fake.getTreeReturns + fake.recordInvocation("GetTree", []interface{}{arg1, arg2, arg3, arg4, arg5}) + fake.getTreeMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4, arg5) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeGit) GetTreeCallCount() int { + fake.getTreeMutex.RLock() + defer fake.getTreeMutex.RUnlock() + return len(fake.getTreeArgsForCall) +} + +func (fake *FakeGit) GetTreeCalls(stub func(context.Context, string, string, string, bool) (*github.Tree, *github.Response, error)) { + fake.getTreeMutex.Lock() + defer fake.getTreeMutex.Unlock() + fake.GetTreeStub = stub +} + +func (fake *FakeGit) GetTreeArgsForCall(i int) (context.Context, string, string, string, bool) { + fake.getTreeMutex.RLock() + defer fake.getTreeMutex.RUnlock() + argsForCall := fake.getTreeArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5 +} + +func (fake *FakeGit) GetTreeReturns(result1 *github.Tree, result2 *github.Response, result3 error) { + fake.getTreeMutex.Lock() + defer fake.getTreeMutex.Unlock() + fake.GetTreeStub = nil + fake.getTreeReturns = struct { + result1 *github.Tree + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeGit) GetTreeReturnsOnCall(i int, result1 *github.Tree, result2 *github.Response, result3 error) { + fake.getTreeMutex.Lock() + defer fake.getTreeMutex.Unlock() + fake.GetTreeStub = nil + if fake.getTreeReturnsOnCall == nil { + fake.getTreeReturnsOnCall = make(map[int]struct { + result1 *github.Tree + result2 *github.Response + result3 error + }) + } + fake.getTreeReturnsOnCall[i] = struct { + result1 *github.Tree + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeGit) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.getBlobRawMutex.RLock() + defer fake.getBlobRawMutex.RUnlock() + fake.getTreeMutex.RLock() + defer fake.getTreeMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeGit) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ githubhttpcache.Git = new(FakeGit) diff --git a/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_rate_limit_source.go b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_rate_limit_source.go new file mode 100644 index 00000000..72e8ba9d --- /dev/null +++ b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_rate_limit_source.go @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +// Code generated by counterfeiter. DO NOT EDIT. +package githubhttpcachefakes + +import ( + "context" + "sync" + + "github.com/gardener/docforge/pkg/readers/repositoryhosts/githubhttpcache" + "github.com/google/go-github/v43/github" +) + +type FakeRateLimitSource struct { + RateLimitsStub func(context.Context) (*github.RateLimits, *github.Response, error) + rateLimitsMutex sync.RWMutex + rateLimitsArgsForCall []struct { + arg1 context.Context + } + rateLimitsReturns struct { + result1 *github.RateLimits + result2 *github.Response + result3 error + } + rateLimitsReturnsOnCall map[int]struct { + result1 *github.RateLimits + result2 *github.Response + result3 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRateLimitSource) RateLimits(arg1 context.Context) (*github.RateLimits, *github.Response, error) { + fake.rateLimitsMutex.Lock() + ret, specificReturn := fake.rateLimitsReturnsOnCall[len(fake.rateLimitsArgsForCall)] + fake.rateLimitsArgsForCall = append(fake.rateLimitsArgsForCall, struct { + arg1 context.Context + }{arg1}) + stub := fake.RateLimitsStub + fakeReturns := fake.rateLimitsReturns + fake.recordInvocation("RateLimits", []interface{}{arg1}) + fake.rateLimitsMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeRateLimitSource) RateLimitsCallCount() int { + fake.rateLimitsMutex.RLock() + defer fake.rateLimitsMutex.RUnlock() + return len(fake.rateLimitsArgsForCall) +} + +func (fake *FakeRateLimitSource) RateLimitsCalls(stub func(context.Context) (*github.RateLimits, *github.Response, error)) { + fake.rateLimitsMutex.Lock() + defer fake.rateLimitsMutex.Unlock() + fake.RateLimitsStub = stub +} + +func (fake *FakeRateLimitSource) RateLimitsArgsForCall(i int) context.Context { + fake.rateLimitsMutex.RLock() + defer fake.rateLimitsMutex.RUnlock() + argsForCall := fake.rateLimitsArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeRateLimitSource) RateLimitsReturns(result1 *github.RateLimits, result2 *github.Response, result3 error) { + fake.rateLimitsMutex.Lock() + defer fake.rateLimitsMutex.Unlock() + fake.RateLimitsStub = nil + fake.rateLimitsReturns = struct { + result1 *github.RateLimits + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRateLimitSource) RateLimitsReturnsOnCall(i int, result1 *github.RateLimits, result2 *github.Response, result3 error) { + fake.rateLimitsMutex.Lock() + defer fake.rateLimitsMutex.Unlock() + fake.RateLimitsStub = nil + if fake.rateLimitsReturnsOnCall == nil { + fake.rateLimitsReturnsOnCall = make(map[int]struct { + result1 *github.RateLimits + result2 *github.Response + result3 error + }) + } + fake.rateLimitsReturnsOnCall[i] = struct { + result1 *github.RateLimits + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRateLimitSource) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.rateLimitsMutex.RLock() + defer fake.rateLimitsMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRateLimitSource) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ githubhttpcache.RateLimitSource = new(FakeRateLimitSource) diff --git a/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_repositories.go b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_repositories.go new file mode 100644 index 00000000..93bcbb72 --- /dev/null +++ b/pkg/readers/repositoryhosts/githubhttpcache/githubhttpcachefakes/fake_repositories.go @@ -0,0 +1,317 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 +// Code generated by counterfeiter. DO NOT EDIT. +package githubhttpcachefakes + +import ( + "context" + "sync" + + "github.com/gardener/docforge/pkg/readers/repositoryhosts/githubhttpcache" + "github.com/google/go-github/v43/github" +) + +type FakeRepositories struct { + GetStub func(context.Context, string, string) (*github.Repository, *github.Response, error) + getMutex sync.RWMutex + getArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + } + getReturns struct { + result1 *github.Repository + result2 *github.Response + result3 error + } + getReturnsOnCall map[int]struct { + result1 *github.Repository + result2 *github.Response + result3 error + } + GetContentsStub func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) + getContentsMutex sync.RWMutex + getContentsArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + arg5 *github.RepositoryContentGetOptions + } + getContentsReturns struct { + result1 *github.RepositoryContent + result2 []*github.RepositoryContent + result3 *github.Response + result4 error + } + getContentsReturnsOnCall map[int]struct { + result1 *github.RepositoryContent + result2 []*github.RepositoryContent + result3 *github.Response + result4 error + } + ListCommitsStub func(context.Context, string, string, *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error) + listCommitsMutex sync.RWMutex + listCommitsArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 *github.CommitsListOptions + } + listCommitsReturns struct { + result1 []*github.RepositoryCommit + result2 *github.Response + result3 error + } + listCommitsReturnsOnCall map[int]struct { + result1 []*github.RepositoryCommit + result2 *github.Response + result3 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRepositories) Get(arg1 context.Context, arg2 string, arg3 string) (*github.Repository, *github.Response, error) { + fake.getMutex.Lock() + ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)] + fake.getArgsForCall = append(fake.getArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + }{arg1, arg2, arg3}) + stub := fake.GetStub + fakeReturns := fake.getReturns + fake.recordInvocation("Get", []interface{}{arg1, arg2, arg3}) + fake.getMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeRepositories) GetCallCount() int { + fake.getMutex.RLock() + defer fake.getMutex.RUnlock() + return len(fake.getArgsForCall) +} + +func (fake *FakeRepositories) GetCalls(stub func(context.Context, string, string) (*github.Repository, *github.Response, error)) { + fake.getMutex.Lock() + defer fake.getMutex.Unlock() + fake.GetStub = stub +} + +func (fake *FakeRepositories) GetArgsForCall(i int) (context.Context, string, string) { + fake.getMutex.RLock() + defer fake.getMutex.RUnlock() + argsForCall := fake.getArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeRepositories) GetReturns(result1 *github.Repository, result2 *github.Response, result3 error) { + fake.getMutex.Lock() + defer fake.getMutex.Unlock() + fake.GetStub = nil + fake.getReturns = struct { + result1 *github.Repository + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRepositories) GetReturnsOnCall(i int, result1 *github.Repository, result2 *github.Response, result3 error) { + fake.getMutex.Lock() + defer fake.getMutex.Unlock() + fake.GetStub = nil + if fake.getReturnsOnCall == nil { + fake.getReturnsOnCall = make(map[int]struct { + result1 *github.Repository + result2 *github.Response + result3 error + }) + } + fake.getReturnsOnCall[i] = struct { + result1 *github.Repository + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRepositories) GetContents(arg1 context.Context, arg2 string, arg3 string, arg4 string, arg5 *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) { + fake.getContentsMutex.Lock() + ret, specificReturn := fake.getContentsReturnsOnCall[len(fake.getContentsArgsForCall)] + fake.getContentsArgsForCall = append(fake.getContentsArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + arg5 *github.RepositoryContentGetOptions + }{arg1, arg2, arg3, arg4, arg5}) + stub := fake.GetContentsStub + fakeReturns := fake.getContentsReturns + fake.recordInvocation("GetContents", []interface{}{arg1, arg2, arg3, arg4, arg5}) + fake.getContentsMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4, arg5) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3, ret.result4 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3, fakeReturns.result4 +} + +func (fake *FakeRepositories) GetContentsCallCount() int { + fake.getContentsMutex.RLock() + defer fake.getContentsMutex.RUnlock() + return len(fake.getContentsArgsForCall) +} + +func (fake *FakeRepositories) GetContentsCalls(stub func(context.Context, string, string, string, *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error)) { + fake.getContentsMutex.Lock() + defer fake.getContentsMutex.Unlock() + fake.GetContentsStub = stub +} + +func (fake *FakeRepositories) GetContentsArgsForCall(i int) (context.Context, string, string, string, *github.RepositoryContentGetOptions) { + fake.getContentsMutex.RLock() + defer fake.getContentsMutex.RUnlock() + argsForCall := fake.getContentsArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5 +} + +func (fake *FakeRepositories) GetContentsReturns(result1 *github.RepositoryContent, result2 []*github.RepositoryContent, result3 *github.Response, result4 error) { + fake.getContentsMutex.Lock() + defer fake.getContentsMutex.Unlock() + fake.GetContentsStub = nil + fake.getContentsReturns = struct { + result1 *github.RepositoryContent + result2 []*github.RepositoryContent + result3 *github.Response + result4 error + }{result1, result2, result3, result4} +} + +func (fake *FakeRepositories) GetContentsReturnsOnCall(i int, result1 *github.RepositoryContent, result2 []*github.RepositoryContent, result3 *github.Response, result4 error) { + fake.getContentsMutex.Lock() + defer fake.getContentsMutex.Unlock() + fake.GetContentsStub = nil + if fake.getContentsReturnsOnCall == nil { + fake.getContentsReturnsOnCall = make(map[int]struct { + result1 *github.RepositoryContent + result2 []*github.RepositoryContent + result3 *github.Response + result4 error + }) + } + fake.getContentsReturnsOnCall[i] = struct { + result1 *github.RepositoryContent + result2 []*github.RepositoryContent + result3 *github.Response + result4 error + }{result1, result2, result3, result4} +} + +func (fake *FakeRepositories) ListCommits(arg1 context.Context, arg2 string, arg3 string, arg4 *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error) { + fake.listCommitsMutex.Lock() + ret, specificReturn := fake.listCommitsReturnsOnCall[len(fake.listCommitsArgsForCall)] + fake.listCommitsArgsForCall = append(fake.listCommitsArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 *github.CommitsListOptions + }{arg1, arg2, arg3, arg4}) + stub := fake.ListCommitsStub + fakeReturns := fake.listCommitsReturns + fake.recordInvocation("ListCommits", []interface{}{arg1, arg2, arg3, arg4}) + fake.listCommitsMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeRepositories) ListCommitsCallCount() int { + fake.listCommitsMutex.RLock() + defer fake.listCommitsMutex.RUnlock() + return len(fake.listCommitsArgsForCall) +} + +func (fake *FakeRepositories) ListCommitsCalls(stub func(context.Context, string, string, *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error)) { + fake.listCommitsMutex.Lock() + defer fake.listCommitsMutex.Unlock() + fake.ListCommitsStub = stub +} + +func (fake *FakeRepositories) ListCommitsArgsForCall(i int) (context.Context, string, string, *github.CommitsListOptions) { + fake.listCommitsMutex.RLock() + defer fake.listCommitsMutex.RUnlock() + argsForCall := fake.listCommitsArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 +} + +func (fake *FakeRepositories) ListCommitsReturns(result1 []*github.RepositoryCommit, result2 *github.Response, result3 error) { + fake.listCommitsMutex.Lock() + defer fake.listCommitsMutex.Unlock() + fake.ListCommitsStub = nil + fake.listCommitsReturns = struct { + result1 []*github.RepositoryCommit + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRepositories) ListCommitsReturnsOnCall(i int, result1 []*github.RepositoryCommit, result2 *github.Response, result3 error) { + fake.listCommitsMutex.Lock() + defer fake.listCommitsMutex.Unlock() + fake.ListCommitsStub = nil + if fake.listCommitsReturnsOnCall == nil { + fake.listCommitsReturnsOnCall = make(map[int]struct { + result1 []*github.RepositoryCommit + result2 *github.Response + result3 error + }) + } + fake.listCommitsReturnsOnCall[i] = struct { + result1 []*github.RepositoryCommit + result2 *github.Response + result3 error + }{result1, result2, result3} +} + +func (fake *FakeRepositories) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.getMutex.RLock() + defer fake.getMutex.RUnlock() + fake.getContentsMutex.RLock() + defer fake.getContentsMutex.RUnlock() + fake.listCommitsMutex.RLock() + defer fake.listCommitsMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRepositories) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ githubhttpcache.Repositories = new(FakeRepositories) diff --git a/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_registry.go b/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_registry.go index 144346a2..12cc5bd8 100644 --- a/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_registry.go +++ b/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_registry.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. @@ -25,11 +25,6 @@ type FakeRegistry struct { result1 repositoryhosts.RepositoryHost result2 error } - LoadStub func(...repositoryhosts.RepositoryHost) - loadMutex sync.RWMutex - loadArgsForCall []struct { - arg1 []repositoryhosts.RepositoryHost - } LogRateLimitsStub func(context.Context) logRateLimitsMutex sync.RWMutex logRateLimitsArgsForCall []struct { @@ -103,38 +98,6 @@ func (fake *FakeRegistry) GetReturnsOnCall(i int, result1 repositoryhosts.Reposi }{result1, result2} } -func (fake *FakeRegistry) Load(arg1 ...repositoryhosts.RepositoryHost) { - fake.loadMutex.Lock() - fake.loadArgsForCall = append(fake.loadArgsForCall, struct { - arg1 []repositoryhosts.RepositoryHost - }{arg1}) - stub := fake.LoadStub - fake.recordInvocation("Load", []interface{}{arg1}) - fake.loadMutex.Unlock() - if stub != nil { - fake.LoadStub(arg1...) - } -} - -func (fake *FakeRegistry) LoadCallCount() int { - fake.loadMutex.RLock() - defer fake.loadMutex.RUnlock() - return len(fake.loadArgsForCall) -} - -func (fake *FakeRegistry) LoadCalls(stub func(...repositoryhosts.RepositoryHost)) { - fake.loadMutex.Lock() - defer fake.loadMutex.Unlock() - fake.LoadStub = stub -} - -func (fake *FakeRegistry) LoadArgsForCall(i int) []repositoryhosts.RepositoryHost { - fake.loadMutex.RLock() - defer fake.loadMutex.RUnlock() - argsForCall := fake.loadArgsForCall[i] - return argsForCall.arg1 -} - func (fake *FakeRegistry) LogRateLimits(arg1 context.Context) { fake.logRateLimitsMutex.Lock() fake.logRateLimitsArgsForCall = append(fake.logRateLimitsArgsForCall, struct { @@ -172,8 +135,6 @@ func (fake *FakeRegistry) Invocations() map[string][][]interface{} { defer fake.invocationsMutex.RUnlock() fake.getMutex.RLock() defer fake.getMutex.RUnlock() - fake.loadMutex.RLock() - defer fake.loadMutex.RUnlock() fake.logRateLimitsMutex.RLock() defer fake.logRateLimitsMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_repository_host.go b/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_repository_host.go index 639d0b56..a9d1eeb3 100644 --- a/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_repository_host.go +++ b/pkg/readers/repositoryhosts/repositoryhostsfakes/fake_repository_host.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/workers/document/document_worker_test.go b/pkg/workers/document/document_worker_test.go index 45ac30b9..a5a59f00 100644 --- a/pkg/workers/document/document_worker_test.go +++ b/pkg/workers/document/document_worker_test.go @@ -83,7 +83,7 @@ var _ = Describe("Document resolving", func() { }) Context("#ProcessNode", func() { - It("", func() { + It("returns correct multisource content", func() { node := &manifest.Node{ FileType: manifest.FileType{ File: "node", @@ -106,7 +106,7 @@ var _ = Describe("Document resolving", func() { Expect(node).To(Equal(nodegot)) }) - It("", func() { + It("returns correct single source content", func() { node := &manifest.Node{ FileType: manifest.FileType{ File: "node", diff --git a/pkg/workers/document/frontmatter/frontmatterfakes/fake_node_meta.go b/pkg/workers/document/frontmatter/frontmatterfakes/fake_node_meta.go index 68e58b7a..6413a874 100644 --- a/pkg/workers/document/frontmatter/frontmatterfakes/fake_node_meta.go +++ b/pkg/workers/document/frontmatter/frontmatterfakes/fake_node_meta.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/workers/document/tests/expected_target.md b/pkg/workers/document/tests/expected_target.md index 2f0f886c..21a501d0 100644 --- a/pkg/workers/document/tests/expected_target.md +++ b/pkg/workers/document/tests/expected_target.md @@ -14,10 +14,10 @@ title: testedFile1 [test3](testedDir/innerDir/testedMarkdownFile5.md) ### Link existing image with relative path -![test4](/baseURL/__resources/gardener-docforge-logo_c3d1ae.png) +![test4](/baseURL/__resources/gardener-docforge-logo_2003fd.png) ### Link existing image with relative path and title -![test5](/baseURL/__resources/gardener-docforge-logo_3b3aab.png "gardener-docforge-logo") +![test5](/baseURL/__resources/gardener-docforge-logo_9d8366.png "gardener-docforge-logo") ### Link outside image ![test6](https://github.com/kubernetes/kubernetes/raw/master/logo/logo.png) diff --git a/pkg/workers/document/tests/expected_target2.md b/pkg/workers/document/tests/expected_target2.md index e9ae7dd9..c1b6cb32 100644 --- a/pkg/workers/document/tests/expected_target2.md +++ b/pkg/workers/document/tests/expected_target2.md @@ -7,7 +7,7 @@ [test2](/integration-test/tested-doc/html-tests/testedHTMLFile2.md) ### Link existing image with relative path -![test3](/baseURL/__resources/gardener-docforge-logo_c3d1ae.png) +![test3](/baseURL/__resources/gardener-docforge-logo_ed3bb1.png) ### Link existing image with relative path and title -![test4](/baseURL/__resources/gardener-docforge-logo_3b3aab.png "gardener-docforge-logo") \ No newline at end of file +![test4](/baseURL/__resources/gardener-docforge-logo_9c54c7.png "gardener-docforge-logo") \ No newline at end of file diff --git a/pkg/workers/downloader/downloader.go b/pkg/workers/downloader/downloader.go index 0acde976..19cf63d5 100644 --- a/pkg/workers/downloader/downloader.go +++ b/pkg/workers/downloader/downloader.go @@ -48,7 +48,7 @@ func NewDownloader(registry repositoryhosts.Registry, writer writers.Writer) (*D // DownloadResourceName create resource name that will be dowloaded from a resource link func DownloadResourceName(resource link.Resource, document string) string { - mdsum := md5.Sum([]byte(resource.GetResourceURL())) + mdsum := md5.Sum([]byte(resource.GetResourceURL() + document)) ext := path.Ext(resource.Path) name := strings.TrimSuffix(path.Base(resource.Path), ext) hash := hex.EncodeToString(mdsum[:])[:6] diff --git a/pkg/workers/downloader/downloaderfakes/fake_interface.go b/pkg/workers/downloader/downloaderfakes/fake_interface.go index e9b67cc3..e34152bb 100644 --- a/pkg/workers/downloader/downloaderfakes/fake_interface.go +++ b/pkg/workers/downloader/downloaderfakes/fake_interface.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/workers/linkresolver/link_resolving.go b/pkg/workers/linkresolver/link_resolving.go index 1fada274..492201f8 100644 --- a/pkg/workers/linkresolver/link_resolving.go +++ b/pkg/workers/linkresolver/link_resolving.go @@ -5,9 +5,11 @@ package linkresolver import ( + "cmp" "fmt" "path" "path/filepath" + "slices" "strings" "github.com/gardener/docforge/cmd/hugo" @@ -37,29 +39,27 @@ type LinkResolver struct { // ResolveLink resolves link func (l *LinkResolver) ResolveLink(destination string, node *manifest.Node, source string) (string, bool, error) { - newDestination := strings.ReplaceAll(destination, "/:v:/", "/%3Av%3A/") - if newDestination != destination { + escapedEmoji := strings.ReplaceAll(destination, "/:v:/", "/%3Av%3A/") + if escapedEmoji != destination { klog.Warningf("escaping : for /:v:/ in link %s for source %s ", destination, source) - destination = newDestination + destination = escapedEmoji } - destinationResource, err := link.NewResource(destination) if err != nil { return "", false, fmt.Errorf("error when parsing link in %s : %w", source, err) } + shouldValidate := true // resolve outside links if destinationResource.IsAbs() { if _, err := l.Repositoryhosts.Get(destination); err != nil { // we don't have a handler for it. Leave it be. return destination, true, nil } - } - shouldValidate := true - // convert destination to absolute link - if !destinationResource.IsAbs() { + } else { + // convert destination to absolute link docHandler, err := l.Repositoryhosts.Get(source) if err != nil { - return "", false, fmt.Errorf("Unexpected error - can't get a handler for already read content: %w", err) + return "", false, fmt.Errorf("unexpected error - can't get a handler for already read content: %w", err) } if destination, err = docHandler.ToAbsLink(source, destination); err != nil { if _, ok := err.(repositoryhosts.ErrResourceNotFound); !ok { @@ -79,17 +79,11 @@ func (l *LinkResolver) ResolveLink(destination string, node *manifest.Node, sour return destination, shouldValidate, nil } // found nodes with this source -> find the shortest path from l.node to one of nodes - minLength := -1 - var destinationNode *manifest.Node - for _, n := range nl { - // TODO: n = findVisibleNode(n) is relative link broken? - relPathBetweenNodes, _ := filepath.Rel(node.Path, n.NodePath()) - pathLength := strings.Count(relPathBetweenNodes, "/") - if pathLength < minLength || minLength == -1 { - minLength = pathLength - destinationNode = n - } - } + destinationNode := slices.MinFunc(nl, func(a, b *manifest.Node) int { + relPathBetweenNodeAndA, _ := filepath.Rel(node.Path, a.NodePath()) + relPathBetweenNodeAndB, _ := filepath.Rel(node.Path, a.NodePath()) + return cmp.Compare(strings.Count(relPathBetweenNodeAndA, "/"), strings.Count(relPathBetweenNodeAndB, "/")) + }) // construct destination from node path destination = strings.ToLower(destinationNode.NodePath()) if l.Hugo.Enabled { diff --git a/pkg/workers/linkresolver/linkresolverfakes/fake_interface.go b/pkg/workers/linkresolver/linkresolverfakes/fake_interface.go index 1797935a..309b561e 100644 --- a/pkg/workers/linkresolver/linkresolverfakes/fake_interface.go +++ b/pkg/workers/linkresolver/linkresolverfakes/fake_interface.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/workers/linkvalidator/linkvalidatorfakes/fake_interface.go b/pkg/workers/linkvalidator/linkvalidatorfakes/fake_interface.go index 2ccf81a0..24f5ef8e 100644 --- a/pkg/workers/linkvalidator/linkvalidatorfakes/fake_interface.go +++ b/pkg/workers/linkvalidator/linkvalidatorfakes/fake_interface.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT. diff --git a/pkg/writers/writersfakes/fake_writer.go b/pkg/writers/writersfakes/fake_writer.go index f022ce55..17eb6698 100644 --- a/pkg/writers/writersfakes/fake_writer.go +++ b/pkg/writers/writersfakes/fake_writer.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors // // SPDX-License-Identifier: Apache-2.0 // Code generated by counterfeiter. DO NOT EDIT.