Skip to content

feat(openapi): add sorted format style - #2118

Merged
ThomasRooney merged 1 commit into
mainfrom
feat/sorted-openapi-format
Aug 18, 2026
Merged

feat(openapi): add sorted format style#2118
ThomasRooney merged 1 commit into
mainfrom
feat/sorted-openapi-format

Conversation

@ThomasRooney

@ThomasRooney ThomasRooney commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

  • add an opt-in sorted formatting style to the OpenAPI transform command
  • support format: { style: sorted } in workflows while preserving readable/default behavior
  • accept JSON or YAML input and emit deterministic JSON with selected array ordering
  • use the released OpenAPI and sdk-gen-config implementations

Usage

speakeasy openapi transform format --style sorted --schema openapi.yaml --out openapi.json
transformations:
  - format:
      style: sorted

Sorted workflow formatting requires JSON output and must be the final transformation.

Validation

  • go test -timeout=30m ./...
  • go build ./...
  • GolangCI-Lint v2.12.2: 0 issues
  • exact byte parity with the reference implementation on a representative OAD

Summary by cubic

Adds an opt-in sorted formatting style to the OpenAPI transform for deterministic JSON output. Previously only a readable format existed; now you can choose readable (default, unchanged) or sorted (deterministic object key and array ordering) with JSON-only output for sorted.

  • CLI: speakeasy openapi transform format accepts --style readable|sorted (default readable). sorted reads JSON or YAML but only writes JSON; requesting YAML output fails with “sorted formatting only supports JSON output.” Unknown styles are rejected before any files are created. YAML output detection for readable is case-insensitive (e.g., .YAML, .YML).
  • Workflows: support format: { style: sorted }. Use JSON output and place the sorted formatting as the final transformation.
  • Sorting specifics: normalizes object keys and reorders arrays under required, parameters, oneOf, anyOf, and allOf, which can affect SDK method signatures and union ordering.
  • Internals: adds FormatSortedDocument and FormatSortedFromReader; the transform runner switches on the selected style and surfaces clear substep messaging.
  • Dependencies: bumps github.com/speakeasy-api/openapi to v1.25.0 and github.com/speakeasy-api/sdk-gen-config to v1.58.0.
  • Tests: unit tests for style validation, case-insensitive YAML extension handling, and YAML→JSON path; integration tests verify deterministic output for both JSON and YAML inputs.

Written for commit a8758e0. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/openapi/transform.go">

<violation number="1" location="cmd/openapi/transform.go:190">
P3: The new sorted-style check lowercases the output path (`strings.ToLower(flags.Out)`) and so rejects `out.YML`/`out.YAML` as YAML, but the shared `setupOutput` computes `yamlOut := utils.HasYAMLExt(out)` on the raw path without normalizing case. As a result, the two format styles make opposite decisions for the same uppercase extension: `--style sorted --out out.YML` is rejected, while `--style readable --out out.YML` is treated as JSON and writes JSON into a `.YML` file. Normalize case in `setupOutput` (or a shared helper) so both styles handle uppercase YAML extensions consistently.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread cmd/openapi/transform.go
if style != workflow.FormatStyleReadable && style != workflow.FormatStyleSorted {
return fmt.Errorf("unsupported format style %q", flags.Style)
}
if style == workflow.FormatStyleSorted && utils.HasYAMLExt(strings.ToLower(flags.Out)) {

@cubic-dev-ai cubic-dev-ai Bot Aug 18, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new sorted-style check lowercases the output path (strings.ToLower(flags.Out)) and so rejects out.YML/out.YAML as YAML, but the shared setupOutput computes yamlOut := utils.HasYAMLExt(out) on the raw path without normalizing case. As a result, the two format styles make opposite decisions for the same uppercase extension: --style sorted --out out.YML is rejected, while --style readable --out out.YML is treated as JSON and writes JSON into a .YML file. Normalize case in setupOutput (or a shared helper) so both styles handle uppercase YAML extensions consistently.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/openapi/transform.go, line 190:

<comment>The new sorted-style check lowercases the output path (`strings.ToLower(flags.Out)`) and so rejects `out.YML`/`out.YAML` as YAML, but the shared `setupOutput` computes `yamlOut := utils.HasYAMLExt(out)` on the raw path without normalizing case. As a result, the two format styles make opposite decisions for the same uppercase extension: `--style sorted --out out.YML` is rejected, while `--style readable --out out.YML` is treated as JSON and writes JSON into a `.YML` file. Normalize case in `setupOutput` (or a shared helper) so both styles handle uppercase YAML extensions consistently.</comment>

<file context>
@@ -166,14 +182,26 @@ func runCleanup(ctx context.Context, flags basicFlagsI) error {
+	if style != workflow.FormatStyleReadable && style != workflow.FormatStyleSorted {
+		return fmt.Errorf("unsupported format style %q", flags.Style)
+	}
+	if style == workflow.FormatStyleSorted && utils.HasYAMLExt(strings.ToLower(flags.Out)) {
+		return fmt.Errorf("sorted formatting only supports JSON output")
+	}
</file context>
Suggested change
if style == workflow.FormatStyleSorted && utils.HasYAMLExt(strings.ToLower(flags.Out)) {
func setupOutput(_ context.Context, out string) (*os.File, bool, error) {
yamlOut := utils.HasYAMLExt(strings.ToLower(out))
if out != "" {
file, err := os.Create(out)
if err != nil {
return nil, yamlOut, err
}
return file, yamlOut, nil
}
return os.Stdout, yamlOut, nil
}
Fix with cubic

@ThomasRooney

Copy link
Copy Markdown
Member Author

Resolved the review finding by making shared YAML extension detection case-insensitive and adding regression coverage for uppercase .YML and .YAML output paths.

@ThomasRooney
ThomasRooney force-pushed the feat/sorted-openapi-format branch from b1bc99a to a8758e0 Compare August 18, 2026 17:10
@ThomasRooney
ThomasRooney merged commit 97f49bf into main Aug 18, 2026
6 checks passed
@ThomasRooney
ThomasRooney deleted the feat/sorted-openapi-format branch August 18, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant