github.com/w3security/vervet/v5@v5.3.1-0.20230618081846-5bd9b5d799dc/internal/cmd/compiler.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  
     8  	"github.com/urfave/cli/v2"
     9  
    10  	"github.com/w3security/vervet/v5/config"
    11  	"github.com/w3security/vervet/v5/internal/compiler"
    12  )
    13  
    14  // BuildCommand is the `vervet build` subcommand.
    15  var BuildCommand = cli.Command{
    16  	Name:      "build",
    17  	Usage:     "Build versioned resources into versioned OpenAPI specs",
    18  	ArgsUsage: "[input resources root] [output api root]",
    19  	Flags: []cli.Flag{
    20  		&cli.StringFlag{
    21  			Name:    "config",
    22  			Aliases: []string{"c", "conf"},
    23  			Usage:   "Project configuration file",
    24  		},
    25  		&cli.BoolFlag{
    26  			Name:  "lint",
    27  			Usage: "Enable linting during build",
    28  			Value: true,
    29  		},
    30  		&cli.StringFlag{
    31  			Name:    "include",
    32  			Aliases: []string{"I"},
    33  			Usage:   "OpenAPI specification to include in build output",
    34  		},
    35  	},
    36  	Action: Build,
    37  }
    38  
    39  // Build compiles versioned resources into versioned API specs.
    40  func Build(ctx *cli.Context) error {
    41  	project, err := projectFromContext(ctx)
    42  	if err != nil {
    43  		return err
    44  	}
    45  	comp, err := compiler.New(ctx.Context, project, ctx.Bool("lint"))
    46  	if err != nil {
    47  		return err
    48  	}
    49  	return comp.BuildAll(ctx.Context)
    50  }
    51  
    52  // LintCommand is the `vervet lint` subcommand.
    53  var LintCommand = cli.Command{
    54  	Name:      "lint",
    55  	Usage:     "Lint  versioned resources",
    56  	ArgsUsage: "[input resources root] [output api root]",
    57  	Flags: []cli.Flag{
    58  		&cli.StringFlag{
    59  			Name:    "config",
    60  			Aliases: []string{"c", "conf"},
    61  			Usage:   "Project configuration file",
    62  		},
    63  	},
    64  	Action: Lint,
    65  }
    66  
    67  // Lint checks versioned resources against linting rules.
    68  func Lint(ctx *cli.Context) error {
    69  	fmt.Fprintln(os.Stderr, `
    70  Vervet is no longer needed to perform API linting.
    71  Update your projects to use 'spectral lint' or 'sweater-comb lint' instead.
    72  `[1:])
    73  	fmt.Fprintln(os.Stderr, `
    74  Attempting to run 'sweater-comb lint' from your current working directory...
    75  `[1:])
    76  	scpath, err := exec.LookPath("sweater-comb")
    77  	if err != nil {
    78  		scpath = "node_modules/.bin/sweater-comb"
    79  	}
    80  	if _, err := os.Stat(scpath); err != nil {
    81  		fmt.Fprintln(os.Stderr, `
    82  Failed to find a 'sweater-comb' executable script.
    83  'npm install @w3security/sweater-comb' and try again?
    84  `[1:])
    85  		return err
    86  	}
    87  	cmd := exec.Command(scpath, "lint")
    88  	cmd.Stdin = os.Stdin
    89  	cmd.Stdout = os.Stdout
    90  	cmd.Stderr = os.Stderr
    91  	return cmd.Run()
    92  }
    93  
    94  func projectFromContext(ctx *cli.Context) (*config.Project, error) {
    95  	var project *config.Project
    96  	if ctx.Args().Len() == 0 {
    97  		var configPath string
    98  		if s := ctx.String("config"); s != "" {
    99  			configPath = s
   100  		} else {
   101  			configPath = ".vervet.yaml"
   102  		}
   103  		f, err := os.Open(configPath)
   104  		if err != nil {
   105  			return nil, fmt.Errorf("failed to open %q: %w", configPath, err)
   106  		}
   107  		defer f.Close()
   108  		project, err = config.Load(f)
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  	} else {
   113  		api := &config.API{
   114  			Resources: []*config.ResourceSet{{
   115  				Path: ctx.Args().Get(0),
   116  			}},
   117  			Output: &config.Output{
   118  				Path: ctx.Args().Get(1),
   119  			},
   120  		}
   121  		if includePath := ctx.String("include"); includePath != "" {
   122  			api.Overlays = append(api.Overlays, &config.Overlay{
   123  				Include: includePath,
   124  			})
   125  		}
   126  		project = &config.Project{
   127  			APIs: map[string]*config.API{
   128  				"": api,
   129  			},
   130  		}
   131  	}
   132  	return project, nil
   133  }