Skip to content

Commit 32013a7

Browse files
committed
internal/ci/checks: rewrite from bash to Go
The tests pass all the same, so the behavior is kept as-is. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Iff18584595ffc01b94c03e8e53622fb845b47766 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1198216 Reviewed-by: Paul Jolly <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent daf98a0 commit 32013a7

File tree

5 files changed

+112
-60
lines changed

5 files changed

+112
-60
lines changed

.github/workflows/trybot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ jobs:
103103
run: go clean -testcache
104104
- if: (matrix.go-version == '1.23.0-rc.2' && matrix.runner == 'ubuntu-22.04')
105105
name: Early git and code sanity checks
106-
run: ./internal/ci/checks/commit.sh
106+
run: go run ./internal/ci/checks
107107
- if: (matrix.go-version == '1.23.0-rc.2' && matrix.runner == 'ubuntu-22.04')
108108
name: Generate
109109
run: go generate ./...

internal/ci/base/github.cue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ checkoutCode: {
100100

101101
earlyChecks: json.#step & {
102102
name: "Early git and code sanity checks"
103-
run: "./internal/ci/checks/commit.sh"
103+
run: "go run ./internal/ci/checks"
104104
}
105105

106106
curlGitHubAPI: {

internal/ci/checks/commit.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2024 CUE Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"bytes"
19+
"fmt"
20+
"log"
21+
"os"
22+
"os/exec"
23+
"regexp"
24+
"slices"
25+
"strings"
26+
)
27+
28+
func main() {
29+
wd, err := os.Getwd()
30+
if err != nil {
31+
log.Fatal(err)
32+
}
33+
if err := checkCommit(wd); err != nil {
34+
log.Fatal(err)
35+
}
36+
}
37+
38+
func checkCommit(dir string) error {
39+
body, err := runCmd(dir, "git", "log", "-1", "--format=%B", "HEAD")
40+
if err != nil {
41+
return err
42+
}
43+
44+
// Ensure that commit messages have a blank second line.
45+
// We know that a commit message must be longer than a single
46+
// line because each commit must be signed-off.
47+
lines := strings.Split(body, "\n")
48+
if len(lines) > 1 && lines[1] != "" {
49+
return fmt.Errorf("The second line of a commit message must be blank")
50+
}
51+
52+
// All authors, including co-authors, must have a signed-off trailer by email.
53+
// Note that trailers are in the form "Name <email>", so grab the email with regexp.
54+
// For now, we require the sorted lists of author and signer emails to match.
55+
// Note that this also fails if a commit isn't signed-off at all.
56+
//
57+
// In Gerrit we already enable a form of this via https://gerrit-review.googlesource.com/Documentation/project-configuration.html#require-signed-off-by,
58+
// but it does not support co-authors nor can it be used when testing GitHub PRs.
59+
authorEmail, err := runCmd(dir, "git", "log", "-1", "--format=%ae")
60+
if err != nil {
61+
return err
62+
}
63+
coauthorList, err := runCmd(dir, "git", "log", "-1", "--format=%(trailers:key=Co-authored-by,valueonly)")
64+
if err != nil {
65+
return err
66+
}
67+
authors := slices.Concat([]string{authorEmail}, extractEmails(coauthorList))
68+
slices.Sort(authors)
69+
authors = slices.Compact(authors)
70+
71+
signerList, err := runCmd(dir, "git", "log", "-1", "--format=%(trailers:key=Signed-off-by,valueonly)")
72+
if err != nil {
73+
return err
74+
}
75+
signers := extractEmails(signerList)
76+
slices.Sort(signers)
77+
signers = slices.Compact(signers)
78+
79+
if !slices.Equal(authors, signers) {
80+
return fmt.Errorf("commit author email addresses %q do not match signed-off-by trailers %q",
81+
authors, signers)
82+
}
83+
84+
return nil
85+
}
86+
87+
func runCmd(dir string, exe string, args ...string) (string, error) {
88+
cmd := exec.Command(exe, args...)
89+
cmd.Dir = dir
90+
out, err := cmd.CombinedOutput()
91+
return string(bytes.TrimSpace(out)), err
92+
}
93+
94+
var rxExtractEmail = regexp.MustCompile(`.*<(.*)\>$`)
95+
96+
func extractEmails(list string) []string {
97+
lines := strings.Split(list, "\n")
98+
var emails []string
99+
for _, line := range lines {
100+
m := rxExtractEmail.FindStringSubmatch(line)
101+
if m == nil {
102+
continue // no match; discard this line
103+
}
104+
emails = append(emails, m[1])
105+
}
106+
return emails
107+
}

internal/ci/checks/commit.sh

Lines changed: 0 additions & 38 deletions
This file was deleted.

internal/ci/checks/commit_test.go

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,13 @@ package main
1717
import (
1818
"io/fs"
1919
"os/exec"
20-
"path/filepath"
21-
"runtime"
2220
"testing"
2321

2422
"github.com/go-quicktest/qt"
2523
"golang.org/x/tools/txtar"
2624
)
2725

2826
func TestCommits(t *testing.T) {
29-
// We are removing the dependency on bash very soon.
30-
if _, err := exec.LookPath("bash"); err != nil {
31-
t.Skipf("cannot find bash: %v", err)
32-
}
33-
if runtime.GOOS != "linux" {
34-
t.Skipf("running only on Linux as others may ship older Bash")
35-
}
36-
37-
scriptPath, err := filepath.Abs("commit.sh")
38-
qt.Assert(t, qt.IsNil(err))
39-
4027
archive, err := txtar.ParseFile("testdata/checks.txtar")
4128
qt.Assert(t, qt.IsNil(err))
4229
archiveFS, err := txtar.FS(archive)
@@ -63,11 +50,9 @@ func TestCommits(t *testing.T) {
6350
for _, name := range passFiles {
6451
t.Run(name, func(t *testing.T) {
6552
dir := setupCommit(t, name)
66-
cmd := exec.Command("bash", scriptPath)
67-
cmd.Dir = dir
68-
data, err := cmd.CombinedOutput()
53+
err = checkCommit(dir)
6954
t.Logf("error: %v", err)
70-
qt.Assert(t, qt.IsNil(err), qt.Commentf("output: %q", data))
55+
qt.Assert(t, qt.IsNil(err))
7156
})
7257
}
7358

@@ -76,9 +61,7 @@ func TestCommits(t *testing.T) {
7661
for _, name := range failFiles {
7762
t.Run(name, func(t *testing.T) {
7863
dir := setupCommit(t, name)
79-
cmd := exec.Command("bash", scriptPath)
80-
cmd.Dir = dir
81-
err = cmd.Run()
64+
err = checkCommit(dir)
8265
t.Logf("error: %v", err)
8366
qt.Assert(t, qt.IsNotNil(err))
8467
})

0 commit comments

Comments
 (0)