Skip to content

Commit

Permalink
internal/ci/checks: rewrite from bash to Go
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
mvdan committed Jul 23, 2024
1 parent daf98a0 commit 32013a7
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/trybot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
run: go clean -testcache
- if: (matrix.go-version == '1.23.0-rc.2' && matrix.runner == 'ubuntu-22.04')
name: Early git and code sanity checks
run: ./internal/ci/checks/commit.sh
run: go run ./internal/ci/checks
- if: (matrix.go-version == '1.23.0-rc.2' && matrix.runner == 'ubuntu-22.04')
name: Generate
run: go generate ./...
Expand Down
2 changes: 1 addition & 1 deletion internal/ci/base/github.cue
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ checkoutCode: {

earlyChecks: json.#step & {
name: "Early git and code sanity checks"
run: "./internal/ci/checks/commit.sh"
run: "go run ./internal/ci/checks"
}

curlGitHubAPI: {
Expand Down
107 changes: 107 additions & 0 deletions internal/ci/checks/commit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2024 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"regexp"
"slices"
"strings"
)

func main() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if err := checkCommit(wd); err != nil {
log.Fatal(err)
}
}

func checkCommit(dir string) error {
body, err := runCmd(dir, "git", "log", "-1", "--format=%B", "HEAD")
if err != nil {
return err
}

// Ensure that commit messages have a blank second line.
// We know that a commit message must be longer than a single
// line because each commit must be signed-off.
lines := strings.Split(body, "\n")
if len(lines) > 1 && lines[1] != "" {
return fmt.Errorf("The second line of a commit message must be blank")
}

// All authors, including co-authors, must have a signed-off trailer by email.
// Note that trailers are in the form "Name <email>", so grab the email with regexp.
// For now, we require the sorted lists of author and signer emails to match.
// Note that this also fails if a commit isn't signed-off at all.
//
// In Gerrit we already enable a form of this via https://gerrit-review.googlesource.com/Documentation/project-configuration.html#require-signed-off-by,
// but it does not support co-authors nor can it be used when testing GitHub PRs.
authorEmail, err := runCmd(dir, "git", "log", "-1", "--format=%ae")
if err != nil {
return err
}
coauthorList, err := runCmd(dir, "git", "log", "-1", "--format=%(trailers:key=Co-authored-by,valueonly)")
if err != nil {
return err
}
authors := slices.Concat([]string{authorEmail}, extractEmails(coauthorList))
slices.Sort(authors)
authors = slices.Compact(authors)

signerList, err := runCmd(dir, "git", "log", "-1", "--format=%(trailers:key=Signed-off-by,valueonly)")
if err != nil {
return err
}
signers := extractEmails(signerList)
slices.Sort(signers)
signers = slices.Compact(signers)

if !slices.Equal(authors, signers) {
return fmt.Errorf("commit author email addresses %q do not match signed-off-by trailers %q",
authors, signers)
}

return nil
}

func runCmd(dir string, exe string, args ...string) (string, error) {
cmd := exec.Command(exe, args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
return string(bytes.TrimSpace(out)), err
}

var rxExtractEmail = regexp.MustCompile(`.*<(.*)\>$`)

func extractEmails(list string) []string {
lines := strings.Split(list, "\n")
var emails []string
for _, line := range lines {
m := rxExtractEmail.FindStringSubmatch(line)
if m == nil {
continue // no match; discard this line
}
emails = append(emails, m[1])
}
return emails
}
38 changes: 0 additions & 38 deletions internal/ci/checks/commit.sh

This file was deleted.

23 changes: 3 additions & 20 deletions internal/ci/checks/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,13 @@ package main
import (
"io/fs"
"os/exec"
"path/filepath"
"runtime"
"testing"

"github.com/go-quicktest/qt"
"golang.org/x/tools/txtar"
)

func TestCommits(t *testing.T) {
// We are removing the dependency on bash very soon.
if _, err := exec.LookPath("bash"); err != nil {
t.Skipf("cannot find bash: %v", err)
}
if runtime.GOOS != "linux" {
t.Skipf("running only on Linux as others may ship older Bash")
}

scriptPath, err := filepath.Abs("commit.sh")
qt.Assert(t, qt.IsNil(err))

archive, err := txtar.ParseFile("testdata/checks.txtar")
qt.Assert(t, qt.IsNil(err))
archiveFS, err := txtar.FS(archive)
Expand All @@ -63,11 +50,9 @@ func TestCommits(t *testing.T) {
for _, name := range passFiles {
t.Run(name, func(t *testing.T) {
dir := setupCommit(t, name)
cmd := exec.Command("bash", scriptPath)
cmd.Dir = dir
data, err := cmd.CombinedOutput()
err = checkCommit(dir)
t.Logf("error: %v", err)
qt.Assert(t, qt.IsNil(err), qt.Commentf("output: %q", data))
qt.Assert(t, qt.IsNil(err))
})
}

Expand All @@ -76,9 +61,7 @@ func TestCommits(t *testing.T) {
for _, name := range failFiles {
t.Run(name, func(t *testing.T) {
dir := setupCommit(t, name)
cmd := exec.Command("bash", scriptPath)
cmd.Dir = dir
err = cmd.Run()
err = checkCommit(dir)
t.Logf("error: %v", err)
qt.Assert(t, qt.IsNotNil(err))
})
Expand Down

0 comments on commit 32013a7

Please sign in to comment.