1
0
mirror of https://github.com/woodpecker-ci/woodpecker.git synced 2025-11-23 21:44:44 +02:00

Switch to GitHub REST API to load changed files (#5618)

Co-authored-by: 6543 <6543@obermui.de>

```
// GitHub has removed commit summaries from Events API payloads from 7th October 2025 onwards.
```

- switch to rest api to load changed files
- added some small wording/var name adjustment
- added test cases using GitHub mock client
This commit is contained in:
Robert Kaussow
2025-10-10 14:19:41 +02:00
committed by GitHub
parent 3a293345dd
commit dd06fbcc61
10 changed files with 528 additions and 217 deletions

View File

@@ -27,7 +27,7 @@ import (
"strings"
"time"
"github.com/google/go-github/v74/github"
"github.com/google/go-github/v75/github"
"github.com/rs/zerolog/log"
"golang.org/x/oauth2"
@@ -40,9 +40,12 @@ import (
"go.woodpecker-ci.org/woodpecker/v3/shared/utils"
)
type contextKey string
const (
defaultURL = "https://github.com" // Default GitHub URL
defaultAPI = "https://api.github.com/" // Default GitHub API URL
defaultURL = "https://github.com" // Default GitHub URL
defaultAPI = "https://api.github.com/" // Default GitHub API URL
githubClientKey contextKey = "github_client"
)
// Opts defines configuration options.
@@ -459,7 +462,13 @@ func (c *client) newConfig() *oauth2.Config {
}
// newClientToken returns the GitHub oauth2 client.
// It first checks if a client is available in the context, otherwise creates a new one.
func (c *client) newClientToken(ctx context.Context, token string) *github.Client {
// Check if a client is already in the context
if ctxClient, ok := ctx.Value(githubClientKey).(*github.Client); ok {
return ctxClient
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
@@ -606,7 +615,7 @@ func (c *client) BranchHead(ctx context.Context, u *model.User, r *model.Repo, b
// Hook parses the post-commit hook from the Request body
// and returns the required data in a standard format.
func (c *client) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model.Pipeline, error) {
pull, repo, pipeline, err := parseHook(r, c.MergeRef)
pull, repo, pipeline, currCommit, prevCommit, err := parseHook(r, c.MergeRef)
if err != nil {
return nil, nil, err
}
@@ -620,11 +629,17 @@ func (c *client) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model
pipeline.Commit = sha
}
if pull != nil && len(pipeline.ChangedFiles) == 0 {
if pull != nil {
pipeline, err = c.loadChangedFilesFromPullRequest(ctx, pull, repo, pipeline)
if err != nil {
return nil, nil, err
}
} else if pipeline.Event == model.EventPush {
// GitHub has removed commit summaries from Events API payloads from 7th October 2025 onwards.
pipeline, err = c.loadChangedFilesFromCommits(ctx, repo, pipeline, prevCommit, currCommit)
if err != nil {
return nil, nil, err
}
}
return repo, pipeline, nil
@@ -647,24 +662,24 @@ func (c *client) loadChangedFilesFromPullRequest(ctx context.Context, pull *gith
return nil, err
}
pipeline.ChangedFiles, err = utils.Paginate(func(page int) ([]string, error) {
opts := &github.ListOptions{Page: page}
fileList := make([]string, 0, 16)
for opts.Page > 0 {
files, resp, err := c.newClientToken(ctx, user.AccessToken).PullRequests.ListFiles(ctx, repo.Owner, repo.Name, pull.GetNumber(), opts)
if err != nil {
return nil, err
}
gh := c.newClientToken(ctx, user.AccessToken)
fileList := make([]string, 0, 16)
for _, file := range files {
fileList = append(fileList, file.GetFilename(), file.GetPreviousFilename())
}
opts.Page = resp.NextPage
opts := &github.ListOptions{Page: 1}
for opts.Page > 0 {
files, resp, err := gh.PullRequests.ListFiles(ctx, repo.Owner, repo.Name, pull.GetNumber(), opts)
if err != nil {
return nil, err
}
return utils.DeduplicateStrings(fileList), nil
}, -1)
for _, file := range files {
fileList = append(fileList, file.GetFilename(), file.GetPreviousFilename())
}
opts.Page = resp.NextPage
}
pipeline.ChangedFiles = utils.DeduplicateStrings(fileList)
return pipeline, err
}
@@ -710,3 +725,65 @@ func (c *client) getTagCommitSHA(ctx context.Context, repo *model.Repo, tagName
}
return tag.GetCommit().GetSHA(), nil
}
func (c *client) loadChangedFilesFromCommits(ctx context.Context, tmpRepo *model.Repo, pipeline *model.Pipeline, curr, prev string) (*model.Pipeline, error) {
_store, ok := store.TryFromContext(ctx)
if !ok {
log.Error().Msg("could not get store from context")
return pipeline, nil
}
switch prev {
case curr:
log.Error().Msg("GitHub push event contains the same commit before and after, no changes detected")
return pipeline, nil
case "0000000000000000000000000000000000000000":
prev = ""
fallthrough
case "":
// For tag events, prev is empty, but we can still fetch the changed files using the current commit
log.Trace().Msg("GitHub tag event, fetching changed files using current commit")
}
repo, err := _store.GetRepoNameFallback(tmpRepo.ForgeRemoteID, tmpRepo.FullName)
if err != nil {
return nil, err
}
user, err := _store.GetUser(repo.UserID)
if err != nil {
return nil, err
}
gh := c.newClientToken(ctx, user.AccessToken)
fileList := make([]string, 0, 16)
if prev == "" {
opts := &github.ListOptions{Page: 1}
for opts.Page > 0 {
commit, resp, err := gh.Repositories.GetCommit(ctx, repo.Owner, repo.Name, curr, &github.ListOptions{})
if err != nil {
return nil, err
}
for _, file := range commit.Files {
fileList = append(fileList, file.GetFilename(), file.GetPreviousFilename())
}
opts.Page = resp.NextPage
}
} else {
opts := &github.ListOptions{Page: 1}
for opts.Page > 0 {
comp, resp, err := gh.Repositories.CompareCommits(ctx, repo.Owner, repo.Name, prev, curr, opts)
if err != nil {
return nil, err
}
for _, file := range comp.Files {
fileList = append(fileList, file.GetFilename(), file.GetPreviousFilename())
}
opts.Page = resp.NextPage
}
}
pipeline.ChangedFiles = utils.DeduplicateStrings(fileList)
return pipeline, err
}