From fe8549b468472e9f7e3e017671710feb6caf4306 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 21 Jul 2026 18:23:32 +0300 Subject: [PATCH 1/5] feat: scaffold openapi spec directory structure Add the openapi/// layout for versioned spec fixtures, with the Petstore OpenAPI 3.1 spec as the first real example. v3.0 and v3.2 are scaffolded empty pending further examples. --- openapi/v3.0/.gitkeep | 0 openapi/v3.1/petstore/spec.yaml | 292 ++++++++++++++++++++++++++++++++ openapi/v3.2/.gitkeep | 0 3 files changed, 292 insertions(+) create mode 100644 openapi/v3.0/.gitkeep create mode 100644 openapi/v3.1/petstore/spec.yaml create mode 100644 openapi/v3.2/.gitkeep diff --git a/openapi/v3.0/.gitkeep b/openapi/v3.0/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/openapi/v3.1/petstore/spec.yaml b/openapi/v3.1/petstore/spec.yaml new file mode 100644 index 0000000..9fdea76 --- /dev/null +++ b/openapi/v3.1/petstore/spec.yaml @@ -0,0 +1,292 @@ +openapi: 3.1.0 +info: + title: Swagger Petstore - OpenAPI 3.1 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.1 specification. + You can find out more about + Swagger at [https://swagger.io](https://swagger.io). + termsOfService: https://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.10 + summary: Pet Store 3.1 + x-namespace: swagger +externalDocs: + description: Find out more about Swagger + url: https://swagger.io +servers: +- url: /api/v31 +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: https://swagger.io +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet. + description: Update an existing pet by Id. + operationId: updatePet + requestBody: + description: Pet object that needs to be updated in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + readOnly: true + "400": + description: Invalid ID supplied + "404": + description: Pet not found + "405": + description: Validation exception + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Add a new pet to the store. + description: Add a new pet to the store. + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON Format + required: + - id + writeOnly: true + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + required: + - id + writeOnly: true + required: true + responses: + "200": + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML Format + readOnly: true + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON format + readOnly: true + "405": + description: Invalid input + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - pet + summary: Find pet by it's identifier. + description: Returns a pet when 0 < ID <= 10. ID > 10 or non-integers will + simulate API error conditions. + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + description: param ID of pet that needs to be fetched + exclusiveMaximum: 10 + exclusiveMinimum: 1 + responses: + "200": + description: The pet + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in JSON format + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: A Pet in XML format + "400": + description: Invalid ID supplied + "404": + description: Pet not found + default: + description: Unexpected error + security: + - petstore_auth: + - write:pets + - read:pets + - api_key: [] +components: + schemas: + Category: + $id: /api/v31/components/schemas/category + description: Category + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + Pet: + $schema: https://json-schema.org/draft/2020-12/schema + description: Pet + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + description: Pet Category + name: + type: string + examples: + - doggie + photoUrls: + type: array + items: + type: string + xml: + name: photoUrl + xml: + wrapped: true + tags: + type: array + items: + $ref: '#/components/schemas/Tag' + xml: + wrapped: true + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + availableInstances: + type: integer + format: int32 + examples: + - "7" + exclusiveMaximum: 10 + exclusiveMinimum: 1 + swagger-extension: true + petDetailsId: + type: integer + format: int64 + $ref: /api/v31/components/schemas/petdetails#pet_details_id + petDetails: + $ref: /api/v31/components/schemas/petdetails + required: + - name + - photoUrls + xml: + name: Pet + PetDetails: + $id: /api/v31/components/schemas/petdetails + $schema: https://json-schema.org/draft/2020-12/schema + $vocabulary: + https://spec.openapis.org/oas/3.1/schema-base: true + properties: + id: + type: integer + format: int64 + $anchor: pet_details_id + examples: + - "10" + category: + $ref: /api/v31/components/schemas/category + description: PetDetails Category + tag: + $ref: /api/v31/components/schemas/tag + xml: + name: PetDetails + Tag: + $id: /api/v31/components/schemas/tag + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://petstore31.swagger.io/oauth/authorize + scopes: + write:pets: modify pets in your account + read:pets: read your pets + mutual_tls: + type: mutualTLS + api_key: + type: apiKey + name: api_key + in: header +webhooks: + newPet: + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + description: Webhook Pet + responses: + "200": + description: Return a 200 status to indicate that the data was received + successfully diff --git a/openapi/v3.2/.gitkeep b/openapi/v3.2/.gitkeep new file mode 100644 index 0000000..e69de29 From 2cda122567c6569f9dfb3afe7954feec9b999c25 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 21 Jul 2026 18:23:49 +0300 Subject: [PATCH 2/5] docs: add project README, contributor guide, and design notes Document the repo's purpose, directory conventions, and CI, and add the dexpace wordmark for branding consistency with the morphic repo. Also records the design decisions behind the scaffold, including the provenance check and fix applied to the seed Petstore spec. --- CLAUDE.md | 92 +++++++++++++ README.md | 67 ++++++++- docs/assets/dexpace-wordmark-dark.svg | 6 + docs/assets/dexpace-wordmark-light.svg | 6 + docs/decisions/2026-07-21-initial-scaffold.md | 128 ++++++++++++++++++ 5 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md create mode 100644 docs/assets/dexpace-wordmark-dark.svg create mode 100644 docs/assets/dexpace-wordmark-light.svg create mode 100644 docs/decisions/2026-07-21-initial-scaffold.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..864ea15 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,92 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +`morphic-test-assets` is a versioned, external corpus of real-world API specs used to test +[morphic](https://github.com/dexpace/morphic), a spec-to-SDK compiler. Morphic parses specs +(OpenAPI today; Swagger, GraphQL, protobuf, TypeSpec planned) into a spec-agnostic intermediate +representation (IR). This repo holds spec fixtures — and eventually their expected IR output — +independent of morphic's own internal `testdata/` golden fixtures, so morphic's tooling and CI can +pull in a growing corpus without vendoring it into the morphic repo itself. + +There is no application code here. This repo is fixtures, directory conventions, and CI. + +## Project status + +Early scaffold. Only OpenAPI v3.x is in scope for now. One seed fixture exists: +`openapi/v3.1/petstore/spec.yaml`. `openapi/v3.0/` and `openapi/v3.2/` are placeholder directories +(`.gitkeep`) awaiting real examples. `expected_ir_file` goldens are deliberately deferred — morphic's +IR format is still evolving (Milestone 1 in the morphic repo), so hand-written or generated goldens +would go stale almost immediately. + +## Directory & naming conventions + +- Spec-type directories live at the repo root: `openapi/` (future: `graphql/`, `protobuf/`, + `typespec/`, ...). +- One directory per spec version inside each: `v3.0/`, `v3.1/`, `v3.2/`. +- One directory per API inside that: `///`. +- Each API directory holds `spec_file` and, once available, `expected_ir_file` (e.g. `spec.yaml` + + `spec-ir.json`). +- APIs split across multiple spec documents use numbered files instead: `spec-1.yml`, `spec-2.yml`, + with matching `spec-1-ir.json`, `spec-2-ir.json` once IR goldens are added. +- Spec files keep their original serialization format (YAML or JSON) when added — don't normalize + or convert format; that's needless transformation and risks drifting from the upstream source. + +## Fixture quality bar + +- A fixture must be genuinely valid OpenAPI, not merely "looks right." Before adding or changing + any spec file, validate it locally: + ```bash + go install github.com/speakeasy-api/openapi/cmd/openapi@v0.0.0-20260622002648-cfc99c3a8316 + openapi spec validate path/to/spec.yaml + ``` + This is the exact same parser library morphic's own compiler depends on + (`github.com/speakeasy-api/openapi`) — if a spec doesn't validate cleanly here, morphic won't + compile it either. +- Don't assume a fixture's origin without checking. A prior fixture + (`openapi/v3.1/petstore/spec.yaml`) was assumed at first to be the official OAI example; tracing + it (diffing against public repos) showed it was actually `speakeasy-api/openapi`'s own test + fixture, and it had one real defect (`$vocabulary` as a bare string instead of the + JSON-Schema-2020-12-required object) that `openapi spec validate` caught and that was fixed in + this repo's copy. Verify provenance and correctness rather than trusting a filename or a + familiar-looking shape. +- Don't gate validity on Spectral or other OpenAPI-3.0-era linters for 3.1 fixtures that use + `$id`-based JSON Schema 2020-12 references — they raise false-positive `invalid-ref` errors + because they don't do full `$id` resolution. `openapi spec validate` is the ground truth here. +- Don't gate on `openapi spec lint` (the same CLI's OWASP/security/style ruleset) either — it's + calibrated for hand-authored new APIs, not a corpus of arbitrary real-world specs, and produces + dozens of stylistic/security-best-practice findings that aren't fixture defects. + +## CI + +- `.github/workflows/lint.yml` (push, PR): `yamllint` on YAML spec files (config: `.yamllint.yml`), + `jq empty` on JSON spec files, `openapi spec validate` on every spec file. +- `.github/workflows/release.yml` (push to `main` only — this single trigger is meant to cover both + "every commit" and "every PR merge," since a merge is itself a push to `main`): bundles every + spec-type directory into `morphic-test-assets-.tar.gz` and publishes it as a + GitHub Release tagged with that same full 40-character commit SHA. +- No semantic versioning: consumers pin to a specific commit SHA / release, not a version range. + +## Repository rules + +These mirror the conventions already in force in `dexpace/morphic` and other dexpace repos, kept +consistent so contributors don't have to context-switch between repos. + +- Branch from `main`: `type/short-desc` (e.g. `feat/github-api-fixture`, + `fix/petstore-vocabulary`, `ci/pin-openapi-cli-version`). +- No CODEOWNERS, PR/issue templates, or CODE_OF_CONDUCT.md exist in this repo — don't assume them + or invent content for files that aren't there. + +## Commits & pull requests + +- **Conventional Commits**: `type(scope): subject`, imperative mood, subject line only (no period, + ≤72 chars). Common types here: `feat` (new fixture or spec-type support), `fix` (fixture defect), + `ci`, `docs`, `chore`. Scope is the touched spec type/API when it narrows things down + (`openapi/petstore`, `ci`) — omit it when the change is repo-wide. +- PR description: Summary / Test plan. Keep PRs scoped to one logical change (one new fixture, or + one CI change) — split unrelated changes into separate PRs. +- Write self-contained, human-framed titles/descriptions. No LLM/session artifacts, no internal + audit/finding IDs, no "remediation"/"audit sweep" framing. State the problem, the change, and the + rationale on their own terms. diff --git a/README.md b/README.md index 3ff8a63..17284a3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,65 @@ -# morphic-test-assets -A set of assets used to test the morphic framework and CLI +

+ + + dexpace + +

+ +

morphic-test-assets

+ +

+ lint + release + License +

+ +A versioned corpus of real-world API specs used to test +[morphic](https://github.com/dexpace/morphic), a spec-to-SDK compiler. Morphic +parses specs (OpenAPI today; Swagger, GraphQL, protobuf, and TypeSpec planned) +into a spec-agnostic Intermediate Representation (IR). This repo holds spec +fixtures — and eventually their expected IR output — independent of morphic's +own internal `testdata/` golden fixtures, so morphic's tooling and CI can pull +in a growing, external corpus. + +## Structure + +One directory per spec format at the repo root, then one per spec version, +then one per API: + +``` +openapi/ + v3.0/ + v3.1/ + petstore/ + spec.yaml + v3.2/ +``` + +Each API directory holds `spec_file` and, once available, `expected_ir_file` +(e.g. `spec.yaml` + `spec-ir.json`). IR golden files are omitted for now — +morphic's IR format is still evolving, so hand-written or generated goldens +would go stale immediately. Spec files keep their original serialization +format (YAML or JSON) rather than being normalized to one. + +APIs split across multiple spec documents use numbered files instead: +`spec-1.yml`, `spec-2.yml`, with matching `spec-1-ir.json`, `spec-2-ir.json` +once IR goldens are added. + +Only OpenAPI v3.x is in scope for now. + +## CI + +- **`lint.yml`** (push, PR): validates every file under `openapi/**` — + `yamllint` for YAML syntax, `jq` for JSON syntax, and + [`openapi spec validate`](https://github.com/speakeasy-api/openapi) (the + same OpenAPI parser morphic itself depends on) for structural OpenAPI + validity. +- **`release.yml`** (push to `main`): bundles every spec-type directory into + `morphic-test-assets-.tar.gz` and publishes it as a GitHub + Release tagged with the full commit SHA. One release per commit on `main`. + +## Versioning + +There's no semantic version — each release is tagged with the exact commit +SHA it was built from. Consumers pin to a specific SHA/release rather than a +version range. diff --git a/docs/assets/dexpace-wordmark-dark.svg b/docs/assets/dexpace-wordmark-dark.svg new file mode 100644 index 0000000..e3a3c8a --- /dev/null +++ b/docs/assets/dexpace-wordmark-dark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/assets/dexpace-wordmark-light.svg b/docs/assets/dexpace-wordmark-light.svg new file mode 100644 index 0000000..727bee4 --- /dev/null +++ b/docs/assets/dexpace-wordmark-light.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/decisions/2026-07-21-initial-scaffold.md b/docs/decisions/2026-07-21-initial-scaffold.md new file mode 100644 index 0000000..08a78c7 --- /dev/null +++ b/docs/decisions/2026-07-21-initial-scaffold.md @@ -0,0 +1,128 @@ +# morphic-test-assets: initial scaffold + +Date: 2026-07-21 +Status: approved + +## Purpose + +`morphic-test-assets` holds versioned, external test fixtures for +[morphic](https://github.com/dexpace/morphic), a spec-to-SDK compiler. Morphic +parses API specs (OpenAPI now; Swagger, GraphQL, protobuf, TypeSpec planned) +into a spec-agnostic Intermediate Representation (IR). This repo provides a +growing corpus of real-world specs — and eventually their expected IR output — +that morphic's own tooling/CI can pull in and compile against, independent of +morphic's internal `testdata/` golden fixtures. + +This pass scopes to: repo/directory structure, CI (lint + release), and one +seed example. IR golden files are deliberately deferred — morphic's IR format +is still evolving (Milestone 1), so hand-written or generated goldens would +likely go stale immediately. + +## Directory structure + +Spec-type directories live at the repo root, one per spec format. Within each, +a directory per spec version, and within that, one directory per API: + +``` +morphic-test-assets/ + openapi/ + v3.0/ (scaffold only, no examples yet) + v3.1/ + petstore/ + spec.yaml + v3.2/ (scaffold only, no examples yet) + .github/workflows/ + lint.yml + release.yml + .yamllint.yml + README.md + LICENSE + .gitignore +``` + +Per-API directory convention: `///{spec_file, +expected_ir_file}`. `expected_ir_file` is omitted until morphic's IR format +stabilizes. Multi-file APIs (e.g. an API split across multiple spec +documents) use numbered files: `spec-1.yml`, `spec-2.yml`, with matching +`spec-1-ir.json`, `spec-2-ir.json` once IR goldens are added. + +Spec files keep their original serialization format (YAML or JSON) rather +than being normalized to one — less transformation, less risk of introducing +drift from the upstream source. + +## Seed content + +One real example this pass: the Swagger Petstore OpenAPI 3.1.0 spec (YAML), +placed at `openapi/v3.1/petstore/spec.yaml`. `v3.0/` and `v3.2/` are +scaffolded with a `.gitkeep` each, since git doesn't track empty directories +and the project structure should be visible in the tree even before real +examples land there. + +Provenance: verified by diff against public GitHub repos, not assumed. It is +**not** an official OAI/OpenAPI-Specification example (that repo has no +`petstore.yaml` under `examples/v3.1/` at any released tag). It's byte-for-byte +identical to `feather-framework/feather-openapi`'s `Petstore/openapi@3.1.yaml`, +and its distinctive fields (`petDetailsId`, `swagger-extension`, `x-namespace`, +the `$id`/`$anchor`/`$vocabulary` usage) match `speakeasy-api/openapi`'s own +test fixture (`openapi/testdata/petstore31.openapi.json`) — the same OpenAPI +parser library morphic depends on. The original had one real defect inherited +from that upstream fixture: `components.schemas.PetDetails.$vocabulary` was a +bare string (`https://spec.openapis.org/oas/3.1/schema-base`), but JSON Schema +2020-12 core requires `$vocabulary` to be an object mapping vocabulary URIs to +booleans. Fixed in our copy to: +```yaml +$vocabulary: + https://spec.openapis.org/oas/3.1/schema-base: true +``` +`openapi spec validate` (see below) confirms 0 errors after the fix. + +## CI: linting (`lint.yml`) + +Triggers: `push` and `pull_request`. + +Runs against every file under `openapi/**`: +- **YAML files** (`*.yaml`, `*.yml`): `yamllint` using `.yamllint.yml` + (relaxed line-length, no required `---` document-start marker — OpenAPI + YAML files don't typically use one). +- **JSON files** (`*.json`, excluding future `*-ir.json` golden files once + those exist): syntax validation (`jq empty` or equivalent). +- **All spec files** (YAML and JSON): `openapi spec validate `, using + the `openapi` CLI from `github.com/speakeasy-api/openapi/cmd/openapi` — the + exact library morphic's own compiler depends on for OpenAPI parsing. + Installed in CI via + `go install github.com/speakeasy-api/openapi/cmd/openapi@v0.0.0-20260622002648-cfc99c3a8316`, + pinned to that exact pseudo-version since the CLI module has no tagged + releases (only the underlying library does — `go list -m -versions` returns + nothing for `cmd/openapi`). Chosen over Spectral: Spectral's default + `spectral:oas` ruleset raised 4 false-positive `invalid-ref` errors against + the seed spec's valid OpenAPI 3.1 `$id`-based JSON Schema 2020-12 + references (Spectral doesn't do full `$id` resolution), while `openapi spec + validate` resolved them correctly and surfaced only the genuine + `$vocabulary` defect above. `openapi spec lint` (the same CLI's opinionated + OWASP/security/style ruleset) was evaluated and rejected as a CI gate — it + produced 78 problems on the seed spec alone (missing rate-limit headers, + numeric ID discouragement, etc.), which isn't a reasonable bar for a corpus + of arbitrary real-world API specs. + +## CI: release (`release.yml`) + +Trigger: `push` to `main` only. This single trigger covers both "every +commit" and "every PR merge" from the original ask, since a PR merge is +itself a push to `main` — a separate PR-merge-specific trigger would create +duplicate releases for the same commit. + +Steps: +1. Checkout. +2. `tar czf morphic-test-assets-.tar.gz openapi/` (bundles every + spec-type directory; currently just `openapi/`). +3. Create a GitHub Release tagged with the **full 40-character commit SHA**, + uploading the tarball as the release asset. + +## Out of scope (this pass) + +- IR golden files / IR generation tooling. +- Additional spec types (GraphQL, protobuf, TypeSpec, Swagger 2.0) — OpenAPI + v3.x only, per the original ask. +- Additional seed examples beyond Petstore. +- Any mechanism for morphic's own CI to consume this repo's releases (no + changes proposed to morphic's `gate.yml`). From 262dfb6d27e897a346c108f557ff739b871b2060 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 21 Jul 2026 18:24:06 +0300 Subject: [PATCH 3/5] ci: add spec lint and release workflows Lint every push and PR: yamllint for YAML syntax, jq for JSON syntax, and openapi spec validate (the same OpenAPI parser morphic itself depends on) for structural validity of every spec file. Release every push to main: bundle the spec directories into a tarball and publish it as a GitHub release tagged with the commit SHA, so there's one release per merged commit. --- .github/workflows/lint.yml | 65 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 27 +++++++++++++++ .yamllint.yml | 10 ++++++ 3 files changed, 102 insertions(+) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .yamllint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..6505289 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,65 @@ +name: lint + +on: + push: + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.26" + + - name: Install openapi CLI + run: go install github.com/speakeasy-api/openapi/cmd/openapi@v0.0.0-20260622002648-cfc99c3a8316 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install yamllint + run: pip install yamllint + + - name: yamllint YAML specs + shell: bash + run: | + shopt -s globstar nullglob + files=(openapi/**/*.yaml openapi/**/*.yml) + if [ ${#files[@]} -eq 0 ]; then + echo "No YAML spec files found, skipping yamllint." + else + yamllint -c .yamllint.yml "${files[@]}" + fi + + - name: Validate JSON spec syntax + shell: bash + run: | + shopt -s globstar nullglob + files=(openapi/**/*.json) + if [ ${#files[@]} -eq 0 ]; then + echo "No JSON spec files found, skipping JSON syntax check." + else + for f in "${files[@]}"; do + jq empty "$f" + done + fi + + - name: Validate OpenAPI specs + shell: bash + run: | + shopt -s globstar nullglob + files=(openapi/**/*.yaml openapi/**/*.yml openapi/**/*.json) + if [ ${#files[@]} -eq 0 ]; then + echo "No spec files found, skipping OpenAPI validation." + else + for f in "${files[@]}"; do + echo "Validating $f" + openapi spec validate "$f" + done + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bc2a285 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +name: release + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build asset bundle + env: + SHA: ${{ github.sha }} + run: tar czf "morphic-test-assets-${SHA}.tar.gz" openapi/ + + - name: Create GitHub release + uses: softprops/action-gh-release@v3 + with: + tag_name: ${{ github.sha }} + name: ${{ github.sha }} + files: morphic-test-assets-${{ github.sha }}.tar.gz diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..18f94b0 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,10 @@ +extends: default + +rules: + line-length: + max: 120 + level: warning + document-start: disable + indentation: + spaces: consistent + indent-sequences: whatever From e56245bcbc949cea3993edde2ec1a4bb4b3c95d5 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 21 Jul 2026 18:26:37 +0300 Subject: [PATCH 4/5] ci: pin action-gh-release to a commit sha Pin the third-party release action to its exact commit rather than the mutable v3 tag, so the workflow can't be affected if that tag is ever repointed upstream. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc2a285..ab5ce52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: run: tar czf "morphic-test-assets-${SHA}.tar.gz" openapi/ - name: Create GitHub release - uses: softprops/action-gh-release@v3 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: tag_name: ${{ github.sha }} name: ${{ github.sha }} From 7a8cf61043cdfc5e2329d00749b5de7c0102151f Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 21 Jul 2026 19:07:03 +0300 Subject: [PATCH 5/5] docs: remove initial scaffold design notes --- docs/decisions/2026-07-21-initial-scaffold.md | 128 ------------------ 1 file changed, 128 deletions(-) delete mode 100644 docs/decisions/2026-07-21-initial-scaffold.md diff --git a/docs/decisions/2026-07-21-initial-scaffold.md b/docs/decisions/2026-07-21-initial-scaffold.md deleted file mode 100644 index 08a78c7..0000000 --- a/docs/decisions/2026-07-21-initial-scaffold.md +++ /dev/null @@ -1,128 +0,0 @@ -# morphic-test-assets: initial scaffold - -Date: 2026-07-21 -Status: approved - -## Purpose - -`morphic-test-assets` holds versioned, external test fixtures for -[morphic](https://github.com/dexpace/morphic), a spec-to-SDK compiler. Morphic -parses API specs (OpenAPI now; Swagger, GraphQL, protobuf, TypeSpec planned) -into a spec-agnostic Intermediate Representation (IR). This repo provides a -growing corpus of real-world specs — and eventually their expected IR output — -that morphic's own tooling/CI can pull in and compile against, independent of -morphic's internal `testdata/` golden fixtures. - -This pass scopes to: repo/directory structure, CI (lint + release), and one -seed example. IR golden files are deliberately deferred — morphic's IR format -is still evolving (Milestone 1), so hand-written or generated goldens would -likely go stale immediately. - -## Directory structure - -Spec-type directories live at the repo root, one per spec format. Within each, -a directory per spec version, and within that, one directory per API: - -``` -morphic-test-assets/ - openapi/ - v3.0/ (scaffold only, no examples yet) - v3.1/ - petstore/ - spec.yaml - v3.2/ (scaffold only, no examples yet) - .github/workflows/ - lint.yml - release.yml - .yamllint.yml - README.md - LICENSE - .gitignore -``` - -Per-API directory convention: `///{spec_file, -expected_ir_file}`. `expected_ir_file` is omitted until morphic's IR format -stabilizes. Multi-file APIs (e.g. an API split across multiple spec -documents) use numbered files: `spec-1.yml`, `spec-2.yml`, with matching -`spec-1-ir.json`, `spec-2-ir.json` once IR goldens are added. - -Spec files keep their original serialization format (YAML or JSON) rather -than being normalized to one — less transformation, less risk of introducing -drift from the upstream source. - -## Seed content - -One real example this pass: the Swagger Petstore OpenAPI 3.1.0 spec (YAML), -placed at `openapi/v3.1/petstore/spec.yaml`. `v3.0/` and `v3.2/` are -scaffolded with a `.gitkeep` each, since git doesn't track empty directories -and the project structure should be visible in the tree even before real -examples land there. - -Provenance: verified by diff against public GitHub repos, not assumed. It is -**not** an official OAI/OpenAPI-Specification example (that repo has no -`petstore.yaml` under `examples/v3.1/` at any released tag). It's byte-for-byte -identical to `feather-framework/feather-openapi`'s `Petstore/openapi@3.1.yaml`, -and its distinctive fields (`petDetailsId`, `swagger-extension`, `x-namespace`, -the `$id`/`$anchor`/`$vocabulary` usage) match `speakeasy-api/openapi`'s own -test fixture (`openapi/testdata/petstore31.openapi.json`) — the same OpenAPI -parser library morphic depends on. The original had one real defect inherited -from that upstream fixture: `components.schemas.PetDetails.$vocabulary` was a -bare string (`https://spec.openapis.org/oas/3.1/schema-base`), but JSON Schema -2020-12 core requires `$vocabulary` to be an object mapping vocabulary URIs to -booleans. Fixed in our copy to: -```yaml -$vocabulary: - https://spec.openapis.org/oas/3.1/schema-base: true -``` -`openapi spec validate` (see below) confirms 0 errors after the fix. - -## CI: linting (`lint.yml`) - -Triggers: `push` and `pull_request`. - -Runs against every file under `openapi/**`: -- **YAML files** (`*.yaml`, `*.yml`): `yamllint` using `.yamllint.yml` - (relaxed line-length, no required `---` document-start marker — OpenAPI - YAML files don't typically use one). -- **JSON files** (`*.json`, excluding future `*-ir.json` golden files once - those exist): syntax validation (`jq empty` or equivalent). -- **All spec files** (YAML and JSON): `openapi spec validate `, using - the `openapi` CLI from `github.com/speakeasy-api/openapi/cmd/openapi` — the - exact library morphic's own compiler depends on for OpenAPI parsing. - Installed in CI via - `go install github.com/speakeasy-api/openapi/cmd/openapi@v0.0.0-20260622002648-cfc99c3a8316`, - pinned to that exact pseudo-version since the CLI module has no tagged - releases (only the underlying library does — `go list -m -versions` returns - nothing for `cmd/openapi`). Chosen over Spectral: Spectral's default - `spectral:oas` ruleset raised 4 false-positive `invalid-ref` errors against - the seed spec's valid OpenAPI 3.1 `$id`-based JSON Schema 2020-12 - references (Spectral doesn't do full `$id` resolution), while `openapi spec - validate` resolved them correctly and surfaced only the genuine - `$vocabulary` defect above. `openapi spec lint` (the same CLI's opinionated - OWASP/security/style ruleset) was evaluated and rejected as a CI gate — it - produced 78 problems on the seed spec alone (missing rate-limit headers, - numeric ID discouragement, etc.), which isn't a reasonable bar for a corpus - of arbitrary real-world API specs. - -## CI: release (`release.yml`) - -Trigger: `push` to `main` only. This single trigger covers both "every -commit" and "every PR merge" from the original ask, since a PR merge is -itself a push to `main` — a separate PR-merge-specific trigger would create -duplicate releases for the same commit. - -Steps: -1. Checkout. -2. `tar czf morphic-test-assets-.tar.gz openapi/` (bundles every - spec-type directory; currently just `openapi/`). -3. Create a GitHub Release tagged with the **full 40-character commit SHA**, - uploading the tarball as the release asset. - -## Out of scope (this pass) - -- IR golden files / IR generation tooling. -- Additional spec types (GraphQL, protobuf, TypeSpec, Swagger 2.0) — OpenAPI - v3.x only, per the original ask. -- Additional seed examples beyond Petstore. -- Any mechanism for morphic's own CI to consume this repo's releases (no - changes proposed to morphic's `gate.yml`).