mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
8379a5338c
* Add verify-starlark build action that returns an error for starlark files with lint Relies on `buildifier` tool. Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Add verify_starlark_step to PR pipeline Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Manually fetch buildifier in curl_image until a new build_image is created Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Format with buildifier Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Remove all unused variables retaining one unused function Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Use snake_case for variable Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Replace deprecated dictionary concatenation with .update() method Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Start adding docstrings for all modules and functions Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Prefer os.WriteFile as ioutil.WriteFile has been deprecated since go 1.16 Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Attempt to document the behavior of the init_enterprise_step Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document test_backend pipeline Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document enterprise_downstream_step Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document the pipeline utility function Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document publish_images_step Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document publish_images_steps Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document enterprise2_pipelines function Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Add tags table for Starlark files. Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document test_frontend Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document windows function Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Add docstrings to verifystarlark functions Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Refactor error handling to be more clear and document complex behavior Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Split errors into execution errors and verification errors Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document all other library functions Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Add local variables to TAGS Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Add blank line between all Args and Returns sections Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Fix new linting errors Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Lint new Starlark files Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Correct buildifier binary mv Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Document the need to set nofile ulimit to at least 2048 Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Update build-container to include buildifier Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Ensure buildifier binary is executable Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Fix valid content test Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Simply return execution error Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Only check files rather than fixing them Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Use updated build-container with executable buildifier Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Test that context cancellation stops execution Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Simplify error handling Return execution errors that short circuit WalkDir rather than separately tracking that error. Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Remove fetching of buildifier binary now that it is in the build-container Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Use build image in verify-starlark step Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Use semver tag The image is the same but uses a semver tag to make it clearer that this is a forward upgrade from the old version. Signed-off-by: Jack Baldry <jack.baldry@grafana.com> * Use node 18 image with buildifier Signed-off-by: Jack Baldry <jack.baldry@grafana.com> --------- Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
func mapSlice[I any, O any](a []I, f func(I) O) []O {
|
|
o := make([]O, len(a))
|
|
for i, e := range a {
|
|
o[i] = f(e)
|
|
}
|
|
return o
|
|
}
|
|
|
|
// VerifyStarlark is the CLI Action for verifying Starlark files in a workspace.
|
|
// It expects a single context argument which is the path to the workspace.
|
|
// The actual verification procedure can return multiple errors which are
|
|
// joined together to be one holistic error for the action.
|
|
func VerifyStarlark(c *cli.Context) error {
|
|
if c.NArg() != 1 {
|
|
var message string
|
|
if c.NArg() == 0 {
|
|
message = "ERROR: missing required argument <workspace path>"
|
|
}
|
|
if c.NArg() > 1 {
|
|
message = "ERROR: too many arguments"
|
|
}
|
|
|
|
if err := cli.ShowSubcommandHelp(c); err != nil {
|
|
return err
|
|
}
|
|
|
|
return cli.Exit(message, 1)
|
|
}
|
|
|
|
workspace := c.Args().Get(0)
|
|
verificationErrs, executionErr := verifyStarlark(c.Context, workspace, buildifierLintCommand)
|
|
if executionErr != nil {
|
|
return executionErr
|
|
}
|
|
|
|
if len(verificationErrs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
noun := "file"
|
|
if len(verificationErrs) > 1 {
|
|
noun += "s"
|
|
}
|
|
|
|
return fmt.Errorf("verification failed for %d %s:\n%s",
|
|
len(verificationErrs),
|
|
noun,
|
|
strings.Join(
|
|
mapSlice(verificationErrs, func(e error) string { return e.Error() }),
|
|
"\n",
|
|
))
|
|
}
|
|
|
|
type commandFunc = func(path string) (command string, args []string)
|
|
|
|
func buildifierLintCommand(path string) (string, []string) {
|
|
return "buildifier", []string{"-lint", "warn", "-mode", "check", path}
|
|
}
|
|
|
|
// verifyStarlark walks all directories starting at provided workspace path and
|
|
// verifies any Starlark files it finds.
|
|
// Starlark files are assumed to end with the .star extension.
|
|
// The verification relies on linting frovided by the 'buildifier' binary which
|
|
// must be in the PATH.
|
|
// A slice of verification errors are returned, one for each file that failed verification.
|
|
// If any execution of the `buildifier` command fails, this is returned separately.
|
|
// commandFn is executed on every Starlark file to determine the command and arguments to be executed.
|
|
// The caller is trusted and it is the callers responsibility to ensure that the resulting command is safe to execute.
|
|
func verifyStarlark(ctx context.Context, workspace string, commandFn commandFunc) ([]error, error) {
|
|
var verificationErrs []error
|
|
|
|
// All errors from filepath.WalkDir are filtered by the fs.WalkDirFunc.
|
|
// Lstat or ReadDir errors are reported as verificationErrors.
|
|
// If any execution of the `buildifier` command fails or if the context is cancelled,
|
|
// it is reported as an error and any verification of subsequent files is skipped.
|
|
err := filepath.WalkDir(workspace, func(path string, d fs.DirEntry, err error) error {
|
|
// Skip verification of the file or files within the directory if there is an error
|
|
// returned by Lstat or ReadDir.
|
|
if err != nil {
|
|
verificationErrs = append(verificationErrs, err)
|
|
return nil
|
|
}
|
|
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
if filepath.Ext(path) == ".star" {
|
|
command, args := commandFn(path)
|
|
// The caller is trusted.
|
|
//nolint:gosec
|
|
cmd := exec.CommandContext(ctx, command, args...)
|
|
cmd.Dir = workspace
|
|
|
|
_, err = cmd.Output()
|
|
if err == nil { // No error, early return.
|
|
return nil
|
|
}
|
|
|
|
// The error returned from cmd.Output() is never wrapped.
|
|
//nolint:errorlint
|
|
if err, ok := err.(*exec.ExitError); ok {
|
|
switch err.ExitCode() {
|
|
// Case comments are informed by the output of `buildifier --help`
|
|
case 1: // syntax errors in input
|
|
verificationErrs = append(verificationErrs, errors.New(string(err.Stderr)))
|
|
return nil
|
|
case 2: // usage errors: invoked incorrectly
|
|
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
|
|
case 3: // unexpected runtime errors: file I/O problems or internal bugs
|
|
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
|
|
case 4: // check mode failed (reformat is needed)
|
|
verificationErrs = append(verificationErrs, errors.New(string(err.Stderr)))
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
|
|
}
|
|
}
|
|
|
|
// Error was not an exit error from the command.
|
|
return fmt.Errorf("command %q: %v", cmd, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return verificationErrs, err
|
|
}
|