1
0
mirror of https://github.com/drone/drone-cli.git synced 2024-11-23 09:21:56 +01:00
drone-cli/drone/lint/lint.go
2023-04-05 17:41:58 +01:00

104 lines
2.4 KiB
Go

package lint
import (
"fmt"
"io/ioutil"
"strings"
"github.com/drone-runners/drone-runner-docker/engine/linter"
"github.com/drone-runners/drone-runner-docker/engine/resource"
"github.com/drone/drone-go/drone"
"github.com/drone/envsubst"
"github.com/drone/runner-go/manifest"
"github.com/urfave/cli"
)
// Command exports the linter command.
var Command = cli.Command{
Name: "lint",
Usage: "lint the yaml file, checks for yaml errors",
ArgsUsage: "<source>",
Action: lint,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "trusted",
Usage: "is the yaml trustable",
},
},
}
type Flags struct {
Build drone.Build
Netrc drone.Netrc
Repo drone.Repo
Stage drone.Stage
System drone.System
}
func lint(c *cli.Context) error {
f := new(Flags)
f.Repo.Trusted = c.Bool("trusted")
var envs map[string]string
path := c.Args().First()
if path == "" {
path = ".drone.yml"
}
rawSource, err := ioutil.ReadFile(path)
if err != nil {
return err
}
// string substitution function ensures that string replacement variables are escaped and quoted if they contain newlines.
subf := func(k string) string {
v := envs[k]
if strings.Contains(v, "\n") {
v = fmt.Sprintf("%q", v)
}
return v
}
// evaluates string replacement expressions and returns an update configuration.
config, err := envsubst.Eval(string(rawSource), subf)
if err != nil {
return err
}
// parse into manifests
inputManifests, err := manifest.ParseString(config)
if err != nil {
return err
}
for _, iter := range inputManifests.Resources {
if iter.GetType() == "docker" {
resource, err := resource.Lookup(iter.GetName(), inputManifests)
if err != nil {
return err
}
// lint the resource and return an error if any linting rules are broken
lint := linter.New()
err = lint.Lint(resource, &f.Repo)
if err != nil {
return err
}
}
}
// now we can check the pipeline dependencies
// get a list of all the pipelines
allStages := map[string]struct{}{}
for _, iter := range inputManifests.Resources {
allStages[iter.GetName()] = struct{}{}
}
// we need to parse the file again into raw resources to access the dependencies
inputRawResources, err := manifest.ParseRawFile(path)
if err != nil {
return err
}
for _, iter := range inputRawResources {
for _, dep := range iter.Deps {
if _, ok := allStages[dep]; !ok {
return fmt.Errorf("Pipeline stage '%s' declares invalid dependency '%s'", iter.Name, dep)
}
}
}
return nil
}