-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Should be no functional change, just a code cleanup
- Loading branch information
1 parent
9db21e7
commit a1b1ef3
Showing
5 changed files
with
687 additions
and
542 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package run | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/speakeasy-api/sdk-gen-config/workflow" | ||
"github.com/speakeasy-api/speakeasy/internal/workflowTracking" | ||
"github.com/speakeasy-api/speakeasy/registry" | ||
) | ||
|
||
type FrozenSource struct { | ||
workflow *Workflow | ||
parentStep *workflowTracking.WorkflowStep | ||
sourceID string | ||
} | ||
|
||
var _ SourceStep = FrozenSource{} | ||
|
||
func NewFrozenSource(w *Workflow, parentStep *workflowTracking.WorkflowStep, sourceID string) FrozenSource { | ||
return FrozenSource{ | ||
workflow: w, | ||
parentStep: parentStep, | ||
sourceID: sourceID, | ||
} | ||
} | ||
|
||
func (f FrozenSource) Do(ctx context.Context, _, _ string) (string, error) { | ||
mergeStep := f.parentStep.NewSubstep("Download OAS from lockfile") | ||
|
||
// Check lockfile exists, produce an error if not | ||
if f.workflow.lockfileOld == nil { | ||
return "", fmt.Errorf("workflow lacks a prior lock file: can't use this on first run") | ||
} | ||
lockSource, ok := f.workflow.lockfileOld.Sources[f.sourceID] | ||
if !ok { | ||
return "", fmt.Errorf("workflow lockfile lacks a reference to source %s: can't use this on first run", f.sourceID) | ||
} | ||
if !registry.IsRegistryEnabled(ctx) { | ||
return "", fmt.Errorf("registry is not enabled for this workspace") | ||
} | ||
if lockSource.SourceBlobDigest == "" || lockSource.SourceRevisionDigest == "" || lockSource.SourceNamespace == "" { | ||
return "", fmt.Errorf("invalid workflow lockfile: namespace = %s blobDigest = %s revisionDigest = %s", lockSource.SourceNamespace, lockSource.SourceBlobDigest, lockSource.SourceRevisionDigest) | ||
} | ||
|
||
var orgSlug, workspaceSlug, registryNamespace string | ||
var err error | ||
|
||
if isSingleRegistrySource(f.workflow.workflow.Sources[f.sourceID]) && f.workflow.workflow.Sources[f.sourceID].Registry == nil { | ||
d := f.workflow.workflow.Sources[f.sourceID].Inputs[0] | ||
registryBreakdown := workflow.ParseSpeakeasyRegistryReference(d.Location.Resolve()) | ||
if registryBreakdown == nil { | ||
return "", fmt.Errorf("failed to parse speakeasy registry reference %s", d.Location) | ||
} | ||
orgSlug = registryBreakdown.OrganizationSlug | ||
workspaceSlug = registryBreakdown.WorkspaceSlug | ||
// odd edge case: we are not migrating the registry location when we're a single registry source. | ||
// Unfortunately can't just fix here as it needs a migration | ||
registryNamespace = lockSource.SourceNamespace | ||
} else if !isSingleRegistrySource(f.workflow.workflow.Sources[f.sourceID]) && f.workflow.workflow.Sources[f.sourceID].Registry == nil { | ||
return "", fmt.Errorf("invalid workflow lockfile: no registry location found for source %s", f.sourceID) | ||
} else if f.workflow.workflow.Sources[f.sourceID].Registry != nil { | ||
orgSlug, workspaceSlug, registryNamespace, _, err = f.workflow.workflow.Sources[f.sourceID].Registry.ParseRegistryLocation() | ||
if err != nil { | ||
return "", fmt.Errorf("error parsing registry location %s: %w", string(f.workflow.workflow.Sources[f.sourceID].Registry.Location), err) | ||
} | ||
} | ||
|
||
if lockSource.SourceNamespace != registryNamespace { | ||
return "", fmt.Errorf("invalid workflow lockfile: namespace %s != %s", lockSource.SourceNamespace, registryNamespace) | ||
} | ||
|
||
registryLocation := fmt.Sprintf( | ||
"%s/%s/%s/%s@%s", | ||
"registry.speakeasyapi.dev", | ||
orgSlug, | ||
workspaceSlug, | ||
lockSource.SourceNamespace, | ||
lockSource.SourceRevisionDigest, | ||
) | ||
|
||
d := workflow.Document{Location: workflow.LocationString(registryLocation)} | ||
docPath, err := registry.ResolveSpeakeasyRegistryBundle(ctx, d, workflow.GetTempDir()) | ||
|
||
if err != nil { | ||
return "", fmt.Errorf("error resolving registry bundle from %s: %w", registryLocation, err) | ||
} | ||
|
||
mergeStep.Succeed() | ||
|
||
return docPath.LocalFilePath, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package run | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/speakeasy-api/sdk-gen-config/workflow" | ||
"github.com/speakeasy-api/speakeasy/internal/log" | ||
"github.com/speakeasy-api/speakeasy/internal/schemas" | ||
"github.com/speakeasy-api/speakeasy/internal/workflowTracking" | ||
"github.com/speakeasy-api/speakeasy/pkg/merge" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
type Merge struct { | ||
workflow *Workflow | ||
parentStep *workflowTracking.WorkflowStep | ||
source workflow.Source | ||
ruleset string | ||
} | ||
|
||
var _ SourceStep = Merge{} | ||
|
||
func NewMerge(w *Workflow, parentStep *workflowTracking.WorkflowStep, source workflow.Source, ruleset string) Merge { | ||
return Merge{ | ||
workflow: w, | ||
parentStep: parentStep, | ||
source: source, | ||
ruleset: ruleset, | ||
} | ||
} | ||
|
||
func (m Merge) Do(ctx context.Context, _, outputLocation string) (string, error) { | ||
mergeStep := m.parentStep.NewSubstep("Merge Documents") | ||
|
||
mergeLocation := m.source.GetTempMergeLocation() | ||
if len(m.source.Overlays) == 0 { | ||
mergeLocation = outputLocation | ||
} | ||
|
||
log.From(ctx).Infof("Merging %d schemas into %s...", len(m.source.Inputs), mergeLocation) | ||
|
||
inSchemas := []string{} | ||
for _, input := range m.source.Inputs { | ||
resolvedPath, err := schemas.ResolveDocument(ctx, input, nil, mergeStep) | ||
if err != nil { | ||
return "", err | ||
} | ||
inSchemas = append(inSchemas, resolvedPath) | ||
} | ||
|
||
mergeStep.NewSubstep(fmt.Sprintf("Merge %d documents", len(m.source.Inputs))) | ||
|
||
if err := mergeDocuments(ctx, inSchemas, mergeLocation, m.ruleset, m.workflow.ProjectDir, m.workflow.SkipGenerateLintReport); err != nil { | ||
return "", err | ||
} | ||
|
||
return mergeLocation, nil | ||
} | ||
|
||
func mergeDocuments(ctx context.Context, inSchemas []string, outFile, defaultRuleset, workingDir string, skipGenerateLintReport bool) error { | ||
if err := os.MkdirAll(filepath.Dir(outFile), os.ModePerm); err != nil { | ||
return err | ||
} | ||
|
||
if err := merge.MergeOpenAPIDocuments(ctx, inSchemas, outFile, defaultRuleset, workingDir, skipGenerateLintReport); err != nil { | ||
return err | ||
} | ||
|
||
log.From(ctx).Printf("Successfully merged %d schemas into %s", len(inSchemas), outFile) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package run | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/speakeasy-api/sdk-gen-config/workflow" | ||
"github.com/speakeasy-api/speakeasy/internal/defaultcodesamples" | ||
"github.com/speakeasy-api/speakeasy/internal/log" | ||
"github.com/speakeasy-api/speakeasy/internal/schemas" | ||
"github.com/speakeasy-api/speakeasy/internal/utils" | ||
"github.com/speakeasy-api/speakeasy/internal/workflowTracking" | ||
"github.com/speakeasy-api/speakeasy/pkg/overlay" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
type Overlay struct { | ||
workflow *Workflow | ||
parentStep *workflowTracking.WorkflowStep | ||
source workflow.Source | ||
outputLocation string | ||
ruleset string | ||
} | ||
|
||
var _ SourceStep = Overlay{} | ||
|
||
func NewOverlay(w *Workflow, parentStep *workflowTracking.WorkflowStep, source workflow.Source, outputLocation, ruleset string) Overlay { | ||
return Overlay{ | ||
workflow: w, | ||
parentStep: parentStep, | ||
source: source, | ||
outputLocation: outputLocation, | ||
ruleset: ruleset, | ||
} | ||
} | ||
|
||
func (o Overlay) Do(ctx context.Context, inputPath, outputPath string) (string, error) { | ||
overlayStep := o.parentStep.NewSubstep("Applying Overlays") | ||
|
||
overlayLocation := outputPath | ||
|
||
log.From(ctx).Infof("Applying %d overlays into %s...", len(o.source.Overlays), overlayLocation) | ||
|
||
var err error | ||
var overlaySchemas []string | ||
for _, overlay := range o.source.Overlays { | ||
overlayFilePath := "" | ||
if overlay.Document != nil { | ||
overlayFilePath, err = schemas.ResolveDocument(ctx, *overlay.Document, nil, overlayStep) | ||
if err != nil { | ||
return "", err | ||
} | ||
} else if overlay.FallbackCodeSamples != nil { | ||
// Make temp file for the overlay output | ||
overlayFilePath = filepath.Join(workflow.GetTempDir(), fmt.Sprintf("fallback_code_samples_overlay_%s.yaml", randStringBytes(10))) | ||
if err := os.MkdirAll(filepath.Dir(overlayFilePath), 0o755); err != nil { | ||
return "", err | ||
} | ||
|
||
err = defaultcodesamples.DefaultCodeSamples(ctx, defaultcodesamples.DefaultCodeSamplesFlags{ | ||
SchemaPath: inputPath, | ||
Language: overlay.FallbackCodeSamples.FallbackCodeSamplesLanguage, | ||
Out: overlayFilePath, | ||
}) | ||
if err != nil { | ||
log.From(ctx).Errorf("failed to generate default code samples: %s", err.Error()) | ||
return "", err | ||
} | ||
} | ||
|
||
overlaySchemas = append(overlaySchemas, overlayFilePath) | ||
} | ||
|
||
overlayStep.NewSubstep(fmt.Sprintf("Apply %d overlay(s)", len(o.source.Overlays))) | ||
|
||
if err := overlayDocument(ctx, inputPath, overlaySchemas, overlayLocation); err != nil { | ||
return "", err | ||
} | ||
|
||
overlayStep.Succeed() | ||
return overlayLocation, nil | ||
} | ||
|
||
func overlayDocument(ctx context.Context, schema string, overlayFiles []string, outFile string) error { | ||
currentBase := schema | ||
if err := os.MkdirAll(workflow.GetTempDir(), os.ModePerm); err != nil { | ||
return err | ||
} | ||
|
||
for _, overlayFile := range overlayFiles { | ||
applyPath := getTempApplyPath(outFile) | ||
|
||
tempOutFile, err := os.Create(applyPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// YamlOut param needs to be based on the eventual output file | ||
if _, err := overlay.Apply(currentBase, overlayFile, utils.HasYAMLExt(outFile), tempOutFile, false, false); err != nil && !strings.Contains(err.Error(), "overlay must define at least one action") { | ||
return err | ||
} | ||
|
||
if err := tempOutFile.Close(); err != nil { | ||
return err | ||
} | ||
|
||
currentBase = applyPath | ||
} | ||
|
||
if err := os.MkdirAll(filepath.Dir(outFile), os.ModePerm); err != nil { | ||
return err | ||
} | ||
|
||
finalTempFile, err := os.Open(currentBase) | ||
if err != nil { | ||
return err | ||
} | ||
defer finalTempFile.Close() | ||
|
||
outFileWriter, err := os.Create(outFile) | ||
if err != nil { | ||
return err | ||
} | ||
defer outFileWriter.Close() | ||
|
||
if _, err := io.Copy(outFileWriter, finalTempFile); err != nil { | ||
return err | ||
} | ||
|
||
log.From(ctx).Successf("Successfully applied %d overlays into %s", len(overlayFiles), outFile) | ||
|
||
return nil | ||
} |
Oops, something went wrong.