Skip to content

Commit b16b07b

Browse files
committed
Sign commits
1 parent f6a8c78 commit b16b07b

File tree

5 files changed

+177
-13
lines changed

5 files changed

+177
-13
lines changed

tools/flakeguard/cmd/make_pr.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,30 @@ import (
2020
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/localdb"
2121
)
2222

23+
const (
24+
githubTokenEnvVar = "GITHUB_TOKEN"
25+
githubTokenFlag = "githubToken"
26+
)
27+
2328
var (
2429
repoPath string
2530
localDBPath string
31+
githubToken string
2632
)
2733

2834
var MakePRCmd = &cobra.Command{
2935
Use: "make-pr",
3036
Short: "Make a PR to skip identified flaky tests",
31-
RunE: makePR,
37+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
38+
if githubToken == "" {
39+
githubToken = os.Getenv(githubTokenEnvVar)
40+
}
41+
if githubToken == "" {
42+
return fmt.Errorf("GitHub token not set, set %s or use --%s", githubTokenEnvVar, githubTokenFlag)
43+
}
44+
return nil
45+
},
46+
RunE: makePR,
3247
}
3348

3449
func makePR(cmd *cobra.Command, args []string) error {
@@ -139,22 +154,15 @@ func makePR(cmd *cobra.Command, args []string) error {
139154
}
140155

141156
fmt.Print("Committing changes, tap your yubikey if it's blinking...")
142-
commitHash, err := targetRepoWorktree.Commit(fmt.Sprintf("Skips flaky %d tests: %s", len(testsToSkip), strings.Join(jiraTickets, ", ")), &git.CommitOptions{})
157+
sha, err := flake_git.MakeSignedCommit(repoPath, fmt.Sprintf("Skips flaky %d tests: %s", len(testsToSkip), strings.Join(jiraTickets, ", ")), branchName, githubToken)
143158
if err != nil {
144159
return fmt.Errorf("failed to commit changes: %w", err)
145160
}
146-
fmt.Println(" ✅")
147-
148-
fmt.Print("Pushing changes to remote, tap your yubikey if it's blinking...")
149-
err = repo.Push(&git.PushOptions{})
150-
if err != nil {
151-
return fmt.Errorf("failed to push changes: %w", err)
152-
}
153-
fmt.Println(" ✅")
161+
fmt.Printf(" %s ✅\n", sha)
154162

155163
ctx := context.Background()
156164
ts := oauth2.StaticTokenSource(
157-
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
165+
&oauth2.Token{AccessToken: githubToken},
158166
)
159167
tc := oauth2.NewClient(ctx, ts)
160168
client := github.NewClient(tc)
@@ -182,7 +190,7 @@ func makePR(cmd *cobra.Command, args []string) error {
182190
skippedTestsPRBody.WriteString(fmt.Sprintf("- Package: `%s`\n", test.Package))
183191
skippedTestsPRBody.WriteString(fmt.Sprintf(" Test: `%s`\n", test.Name))
184192
skippedTestsPRBody.WriteString(fmt.Sprintf(" Ticket: [%s](https://%s/browse/%s)\n", test.JiraTicket, os.Getenv("JIRA_DOMAIN"), test.JiraTicket))
185-
skippedTestsPRBody.WriteString(fmt.Sprintf(" [View skip in PR](https://github.com/%s/%s/pull/%s/files#diff-%sL%d)\n\n", owner, repoName, branchName, commitHash, test.Line))
193+
skippedTestsPRBody.WriteString(fmt.Sprintf(" [View skip in PR](https://github.com/%s/%s/pull/%s/files#diff-%sL%d)\n\n", owner, repoName, branchName, sha, test.Line))
186194
} else if test.AlreadySkipped {
187195
if alreadySkippedTestsPRBody.Len() == 0 {
188196
alreadySkippedTestsPRBody.WriteString("## Tests That Were Already Skipped\n\n")
@@ -248,4 +256,5 @@ func makePR(cmd *cobra.Command, args []string) error {
248256

249257
func init() {
250258
MakePRCmd.Flags().StringVarP(&repoPath, "repoPath", "r", ".", "Local path to the repository to make the PR for")
259+
MakePRCmd.Flags().StringVarP(&githubToken, githubTokenFlag, "t", "", fmt.Sprintf("GitHub token to use for the PR (can be set with %s)", githubTokenEnvVar))
251260
}

tools/flakeguard/git/git.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package git
22

33
import (
44
"bytes"
5+
"context"
6+
"encoding/base64"
57
"fmt"
8+
"io"
69
"os"
710
"os/exec"
811
"path/filepath"
@@ -11,7 +14,9 @@ import (
1114

1215
"github.com/go-git/go-git/v5"
1316
"github.com/go-git/go-git/v5/plumbing"
17+
"github.com/shurcooL/githubv4"
1418
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/utils"
19+
"golang.org/x/oauth2"
1520
)
1621

1722
// FindChangedFiles executes a git diff against a specified base reference and pipes the output through a user-defined grep command or sequence.
@@ -261,3 +266,147 @@ func GetOwnerRepoDefaultBranchFromLocalRepo(repoPath string) (owner, repoName, d
261266

262267
return owner, repoName, defaultBranch, nil
263268
}
269+
270+
// MakeSignedCommit adds all changes to a repo and creates a signed commit for GitHub
271+
func MakeSignedCommit(repoPath, commitMessage, branch, githubToken string) (string, error) {
272+
tok := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
273+
token := oauth2.NewClient(context.Background(), tok)
274+
graphqlClient := githubv4.NewClient(token)
275+
276+
repo, err := git.PlainOpen(repoPath)
277+
if err != nil {
278+
return "", err
279+
}
280+
281+
// Code mostly stolen from https://github.com/planetscale/ghcommit/tree/main
282+
283+
// process added / modified files:
284+
worktree, err := repo.Worktree()
285+
if err != nil {
286+
return "", err
287+
}
288+
289+
// Get the status of all files in the worktree
290+
status, err := worktree.Status()
291+
if err != nil {
292+
return "", err
293+
}
294+
295+
additions := []githubv4.FileAddition{}
296+
deletions := []githubv4.FileDeletion{}
297+
298+
// Process each file based on its status
299+
for filePath, fileStatus := range status {
300+
switch fileStatus.Staging {
301+
case git.Added, git.Modified:
302+
// File is added or modified - add to additions
303+
enc, err := base64EncodeFile(filepath.Join(repoPath, filePath))
304+
if err != nil {
305+
return "", err
306+
}
307+
additions = append(additions, githubv4.FileAddition{
308+
Path: githubv4.String(filePath),
309+
Contents: githubv4.Base64String(enc),
310+
})
311+
case git.Deleted:
312+
// File is deleted - add to deletions
313+
deletions = append(deletions, githubv4.FileDeletion{
314+
Path: githubv4.String(filePath),
315+
})
316+
}
317+
318+
// Also check worktree status (unstaged changes)
319+
switch fileStatus.Worktree {
320+
case git.Modified:
321+
// Only add if not already processed from staging
322+
if fileStatus.Staging != git.Added && fileStatus.Staging != git.Modified {
323+
enc, err := base64EncodeFile(filepath.Join(repoPath, filePath))
324+
if err != nil {
325+
return "", err
326+
}
327+
additions = append(additions, githubv4.FileAddition{
328+
Path: githubv4.String(filePath),
329+
Contents: githubv4.Base64String(enc),
330+
})
331+
}
332+
case git.Deleted:
333+
// Only add if not already processed from staging
334+
if fileStatus.Staging != git.Deleted {
335+
deletions = append(deletions, githubv4.FileDeletion{
336+
Path: githubv4.String(filePath),
337+
})
338+
}
339+
}
340+
}
341+
342+
var m struct {
343+
CreateCommitOnBranch struct {
344+
Commit struct {
345+
URL string `graphql:"url"`
346+
OID string `graphql:"oid"`
347+
Additions int `graphql:"additions"`
348+
Deletions int `graphql:"deletions"`
349+
}
350+
} `graphql:"createCommitOnBranch(input:$input)"`
351+
}
352+
353+
splitMsg := strings.SplitN(commitMessage, "\n", 2)
354+
headline := splitMsg[0]
355+
body := ""
356+
if len(splitMsg) > 1 {
357+
body = splitMsg[1]
358+
}
359+
360+
owner, repoName, _, err := GetOwnerRepoDefaultBranchFromLocalRepo(repoPath)
361+
if err != nil {
362+
return "", err
363+
}
364+
365+
// Get HEAD reference to get the current commit hash
366+
headRef, err := repo.Head()
367+
if err != nil {
368+
return "", fmt.Errorf("failed to get HEAD reference: %w", err)
369+
}
370+
expectedHeadOid := headRef.Hash().String()
371+
// create the $input struct for the graphQL createCommitOnBranch mutation request:
372+
input := githubv4.CreateCommitOnBranchInput{
373+
Branch: githubv4.CommittableBranch{
374+
RepositoryNameWithOwner: githubv4.NewString(githubv4.String(fmt.Sprintf("%s/%s", owner, repoName))),
375+
BranchName: githubv4.NewString(githubv4.String(branch)),
376+
},
377+
Message: githubv4.CommitMessage{
378+
Headline: githubv4.String(headline),
379+
Body: githubv4.NewString(githubv4.String(body)),
380+
},
381+
FileChanges: &githubv4.FileChanges{
382+
Additions: &additions,
383+
Deletions: &deletions,
384+
},
385+
ExpectedHeadOid: githubv4.GitObjectID(expectedHeadOid),
386+
}
387+
388+
if err := graphqlClient.Mutate(context.Background(), &m, input, nil); err != nil {
389+
return "", err
390+
}
391+
392+
return m.CreateCommitOnBranch.Commit.OID, nil
393+
}
394+
395+
func base64EncodeFile(path string) (string, error) {
396+
in, err := os.Open(path)
397+
if err != nil {
398+
return "", err
399+
}
400+
defer in.Close() // nolint: errcheck
401+
402+
buf := bytes.Buffer{}
403+
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
404+
405+
if _, err := io.Copy(encoder, in); err != nil {
406+
return "", err
407+
}
408+
if err := encoder.Close(); err != nil {
409+
return "", err
410+
}
411+
return buf.String(), nil
412+
}

tools/flakeguard/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/google/go-github/v72 v72.0.0
1414
github.com/google/uuid v1.6.0
1515
github.com/rs/zerolog v1.34.0
16+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
1617
github.com/spf13/cobra v1.9.1
1718
github.com/stretchr/testify v1.10.0
1819
golang.org/x/oauth2 v0.28.0
@@ -56,6 +57,7 @@ require (
5657
github.com/pmezard/go-difflib v1.0.0 // indirect
5758
github.com/rivo/uniseg v0.4.7 // indirect
5859
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
60+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
5961
github.com/skeema/knownhosts v1.3.1 // indirect
6062
github.com/spf13/pflag v1.0.6 // indirect
6163
github.com/trivago/tgo v1.0.7 // indirect

tools/flakeguard/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6
125125
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
126126
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
127127
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
128+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
129+
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
130+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
131+
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
128132
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
129133
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
130134
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=

tools/flakeguard/golang/golang.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ func SkipTests(repoPath string, testsToSkip []*SkipTest) error {
283283
// Let the outer loop know that we found the test to skip
284284
found = testToSkip.SimplySkipped || testToSkip.AlreadySkipped
285285
if testToSkip.SimplySkipped {
286-
log.Debug().
286+
log.Info().
287287
Str("test", testToSkip.Name).
288288
Str("file", testToSkip.File).
289289
Int("line", testToSkip.Line).

0 commit comments

Comments
 (0)