Skip to content

Commit

Permalink
Merge pull request #354 from snyk/feat/add-meta-build-command
Browse files Browse the repository at this point in the history
feat: change build to be a meta-build command, invoking old and simple build
  • Loading branch information
tinygrasshopper authored Aug 8, 2024
2 parents dcdba9a + 30bc0b2 commit a66343d
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 70 deletions.
1 change: 1 addition & 0 deletions internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ var CLIApp = cli.App{
Commands: []*cli.Command{
&BackstageCommand,
&BuildCommand,
&RetroBuildCommand,
&SimpleBuildCommand,
&FilterCommand,
&GenerateCommand,
Expand Down
109 changes: 78 additions & 31 deletions internal/cmd/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,74 +6,121 @@ import (

"github.com/urfave/cli/v2"

"github.com/snyk/vervet/v7"
"github.com/snyk/vervet/v7/config"
"github.com/snyk/vervet/v7/internal/compiler"
"github.com/snyk/vervet/v7/internal/simplebuild"
)

var defaultPivotDate = vervet.MustParseVersion("2024-09-01")
var pivotDateCLIFlagName = "pivot-version"

var buildFlags = []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c", "conf"},
Usage: "Project configuration file",
},
&cli.StringFlag{
Name: "include",
Aliases: []string{"I"},
Usage: "OpenAPI specification to include in build output",
},
&cli.StringFlag{
Name: pivotDateCLIFlagName,
Aliases: []string{"P"},
Usage: fmt.Sprintf(
"Pivot version after which new strategy versioning is used."+
" Flag for testing only, recommend to use the default date(%s)", defaultPivotDate.String()),
Value: defaultPivotDate.String(),
},
}

// BuildCommand is the `vervet build` subcommand.
var BuildCommand = cli.Command{
Name: "build",
Usage: "Build versioned resources into versioned OpenAPI specs",
ArgsUsage: "[input resources root] [output api root]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c", "conf"},
Usage: "Project configuration file",
},
&cli.StringFlag{
Name: "include",
Aliases: []string{"I"},
Usage: "OpenAPI specification to include in build output",
},
},
Action: Build,
Flags: buildFlags,
Action: CombinedBuild,
}

// RetroBuild is the `vervet build` subcommand.
var RetroBuildCommand = cli.Command{
Name: "retrobuild",
Usage: "Build versioned resources into versioned OpenAPI specs",
ArgsUsage: "[input resources root] [output api root]",
Flags: buildFlags,
Action: RetroBuild,
}

var SimpleBuildCommand = cli.Command{
Name: "simplebuild",
Usage: "Build versioned resources into versioned OpenAPI specs",
ArgsUsage: "[input resources root]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c", "conf"},
Usage: "Project configuration file",
},
&cli.StringFlag{
Name: "include",
Aliases: []string{"I"},
Usage: "OpenAPI specification to include in build output",
},
},
Action: SimpleBuild,
Flags: buildFlags,
Action: SimpleBuild,
}

// SimpleBuild compiles versioned resources into versioned API specs using the rolled up versioning strategy.
func SimpleBuild(ctx *cli.Context) error {
project, err := projectFromContext(ctx)
if err != nil {
return err
}
err = simplebuild.Build(ctx.Context, project)
pivotDate, err := parsePivotDate(ctx)
if err != nil {
return fmt.Errorf("failed to parse pivot date %q: %w", pivotDate, err)
}

err = simplebuild.Build(ctx.Context, project, pivotDate, false)
return err
}

// Build compiles versioned resources into versioned API specs.
func Build(ctx *cli.Context) error {
// CombinedBuild compiles versioned resources into versioned API specs
// invokes retorbuild and simplebuild based on the context.
func CombinedBuild(ctx *cli.Context) error {
project, err := projectFromContext(ctx)
if err != nil {
return err
}
pivotDate, err := parsePivotDate(ctx)
if err != nil {
return fmt.Errorf("failed to parse pivot date %q: %w", pivotDate, err)
}

comp, err := compiler.New(ctx.Context, project)
if err != nil {
return err
}
err = comp.BuildAll(ctx.Context)
err = comp.BuildAll(ctx.Context, pivotDate)
if err != nil {
return err
}

return simplebuild.Build(ctx.Context, project, pivotDate, true)
}

func parsePivotDate(ctx *cli.Context) (vervet.Version, error) {
return vervet.ParseVersion(ctx.String(pivotDateCLIFlagName))
}

// RetroBuild compiles versioned resources into versioned API specs using the older versioning strategy.
// This is used for regenerating old versioned API specs only.
func RetroBuild(ctx *cli.Context) error {
project, err := projectFromContext(ctx)
if err != nil {
return err
}
pivotDate, err := parsePivotDate(ctx)
if err != nil {
return fmt.Errorf("failed to parse pivot date %q: %w", pivotDate, err)
}
comp, err := compiler.New(ctx.Context, project)
if err != nil {
return err
}
return nil
return comp.BuildAll(ctx.Context, pivotDate)
}

func projectFromContext(ctx *cli.Context) (*config.Project, error) {
Expand Down
31 changes: 15 additions & 16 deletions internal/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,9 @@ func ResourceSpecFiles(rcConfig *config.ResourceSet) ([]string, error) {
return files.LocalFSSource{}.Match(rcConfig)
}

func (c *Compiler) apisEach(ctx context.Context, f func(ctx context.Context, apiName string) error) error {
var errs error
for apiName := range c.apis {
err := f(ctx, apiName)
if err != nil {
errs = multierr.Append(errs, err)
}
}
return errs
}

// Build builds an aggregate versioned OpenAPI spec for a specific API by name
// in the project.
func (c *Compiler) Build(ctx context.Context, apiName string) error {
func (c *Compiler) Build(apiName string, stopVersion vervet.Version) error {
api, ok := c.apis[apiName]
if !ok {
return fmt.Errorf("api not found (apis.%s)", apiName)
Expand All @@ -156,7 +145,7 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error {
log.Printf("compiling API %s to output versions", apiName)
var versionSpecFiles []string
for rcIndex, rc := range api.resources {
specVersions, err := vervet.LoadSpecVersionsFileset(rc.sourceFiles) //nolint:contextcheck //acked
specVersions, err := vervet.LoadSpecVersionsFileset(rc.sourceFiles)
if err != nil {
return fmt.Errorf("failed to load spec versions: %+v (apis.%s.resources[%d])",
err, apiName, rcIndex)
Expand All @@ -172,6 +161,9 @@ func (c *Compiler) Build(ctx context.Context, apiName string) error {
version,
))
}
if version.Compare(stopVersion) >= 0 {
continue
}

spec, err := specVersions.At(version)
if err == vervet.ErrNoMatchingVersion {
Expand Down Expand Up @@ -298,7 +290,14 @@ import "embed"
var Versions embed.FS
`[1:]))

// BuildAll builds all APIs in the project.
func (c *Compiler) BuildAll(ctx context.Context) error {
return c.apisEach(ctx, c.Build)
// BuildAll builds all APIs in the project, before the stop version.
func (c *Compiler) BuildAll(ctx context.Context, stopVersion vervet.Version) error {
var errs error
for apiName := range c.apis {
err := c.Build(apiName, stopVersion) //nolint:contextcheck // TODO: fix contextcheck in separate PR
if err != nil {
errs = multierr.Append(errs, err)
}
}
return errs
}
5 changes: 3 additions & 2 deletions internal/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

qt "github.com/frankban/quicktest"

"github.com/snyk/vervet/v7"
"github.com/snyk/vervet/v7/config"
"github.com/snyk/vervet/v7/testdata"
)
Expand Down Expand Up @@ -105,7 +106,7 @@ func TestCompilerSmoke(t *testing.T) {
c.Assert(restApi.output, qt.Not(qt.IsNil))

// Build stage
err = compiler.BuildAll(ctx)
err = compiler.BuildAll(ctx, vervet.MustParseVersion("2024-06-01"))
c.Assert(err, qt.IsNil)

// Verify created files/folders are as expected
Expand Down Expand Up @@ -141,7 +142,7 @@ func TestCompilerSmokePaths(t *testing.T) {
c.Assert(err, qt.IsNil)

// Build stage
err = compiler.BuildAll(ctx)
err = compiler.BuildAll(ctx, vervet.MustParseVersion("2024-06-01"))
c.Assert(err, qt.IsNil)

refOutputPath := testdata.Path("output")
Expand Down
25 changes: 21 additions & 4 deletions internal/simplebuild/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import (
"github.com/snyk/vervet/v7/internal/files"
)

func Build(ctx context.Context, project *config.Project) error {
// Build compiles the versioned resources in a project configuration based on
// simplified versioning rules, after the start date.
func Build(ctx context.Context, project *config.Project, startDate vervet.Version, appendOutputFiles bool) error {
if time.Now().Before(startDate.Date) {
return nil
}
for _, apiConfig := range project.APIs {
operations, err := LoadPaths(ctx, apiConfig)
if err != nil {
Expand All @@ -23,7 +28,8 @@ func Build(ctx context.Context, project *config.Project) error {
for _, op := range operations {
op.Annotate()
}
docs, err := operations.Build()

docs, err := operations.Build(startDate)
if err != nil {
return err
}
Expand All @@ -34,7 +40,7 @@ func Build(ctx context.Context, project *config.Project) error {
}

if apiConfig.Output != nil {
err = docs.WriteOutputs(*apiConfig.Output)
err = docs.WriteOutputs(*apiConfig.Output, appendOutputFiles)
if err != nil {
return err
}
Expand Down Expand Up @@ -64,8 +70,9 @@ type VersionedDoc struct {
}
type DocSet []VersionedDoc

func (ops Operations) Build() (DocSet, error) {
func (ops Operations) Build(startVersion vervet.Version) (DocSet, error) {
versionDates := ops.VersionDates()
versionDates = filterVersionByStartDate(versionDates, startVersion.Date)
output := make(DocSet, len(versionDates))
for idx, versionDate := range versionDates {
output[idx] = VersionedDoc{
Expand All @@ -88,6 +95,16 @@ func (ops Operations) Build() (DocSet, error) {
return output, nil
}

func filterVersionByStartDate(dates []time.Time, startDate time.Time) []time.Time {
resultDates := []time.Time{startDate}
for _, d := range dates {
if d.After(startDate) {
resultDates = append(resultDates, d)
}
}
return resultDates
}

func (ops Operations) VersionDates() []time.Time {
versionSet := map[time.Time]struct{}{}
for _, opSet := range ops {
Expand Down
Loading

0 comments on commit a66343d

Please sign in to comment.