package recipe import ( "fmt" "os" "strconv" "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/tagcmp" "github.com/docker/distribution/reference" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) var recipeLintCommand = &cli.Command{ Name: "lint", Usage: "Lint a recipe", Aliases: []string{"l"}, ArgsUsage: "", Action: func(c *cli.Context) error { recipe := internal.ValidateRecipe(c) expectedVersion := false if recipe.Config.Version == "3.8" { expectedVersion = true } envSampleProvided := false envSample := fmt.Sprintf("%s/%s/.env.sample", config.APPS_DIR, recipe.Name) if _, err := os.Stat(envSample); !os.IsNotExist(err) { envSampleProvided = true } else if err != nil { logrus.Fatal(err) } serviceNamedApp := false traefikEnabled := false healthChecksForAllServices := true allImagesTagged := true noUnstableTags := true semverLikeTags := true for _, service := range recipe.Config.Services { if service.Name == "app" { serviceNamedApp = true } for label := range service.Deploy.Labels { if label == "traefik.enable" { if service.Deploy.Labels[label] == "true" { traefikEnabled = true } } } img, err := reference.ParseNormalizedNamed(service.Image) if err != nil { logrus.Fatal(err) } if reference.IsNameOnly(img) { allImagesTagged = false } var tag string switch img.(type) { case reference.NamedTagged: tag = img.(reference.NamedTagged).Tag() case reference.Named: noUnstableTags = false } if tag == "latest" { noUnstableTags = false } if !tagcmp.IsParsable(tag) { semverLikeTags = false } if service.HealthCheck == nil { healthChecksForAllServices = false } } tableCol := []string{"rule", "satisfied"} table := formatter.CreateTable(tableCol) table.Append([]string{"compose files have the expected version", strconv.FormatBool(expectedVersion)}) table.Append([]string{"environment configuration is provided", strconv.FormatBool(envSampleProvided)}) table.Append([]string{"recipe contains a service named 'app'", strconv.FormatBool(serviceNamedApp)}) table.Append([]string{"traefik routing enabled on at least one service", strconv.FormatBool(traefikEnabled)}) table.Append([]string{"all services have a healthcheck enabled", strconv.FormatBool(healthChecksForAllServices)}) table.Append([]string{"all images are using a tag", strconv.FormatBool(allImagesTagged)}) table.Append([]string{"no usage of unstable 'latest' tags", strconv.FormatBool(noUnstableTags)}) table.Append([]string{"all tags are using a semver-like format", strconv.FormatBool(semverLikeTags)}) table.Render() return nil }, BashComplete: autocomplete.RecipeNameComplete, }