Skip to content

Commit b22ae9e

Browse files
committed
feat: initial commit
Add Petstore OpenAPI example demonstrating Contractual workflow: - OpenAPI 3.0.3 Petstore spec with CRUD operations - Contractual configuration and initial snapshot - GitHub Actions workflows for PR checks and releases - Comprehensive README with workflow documentation
0 parents  commit b22ae9e

10 files changed

Lines changed: 916 additions & 0 deletions

File tree

.contractual/changesets/.gitkeep

Whitespace-only changes.

.contractual/snapshots/.gitkeep

Whitespace-only changes.
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
openapi: 3.0.3
2+
info:
3+
title: Petstore API
4+
description: A simple API for managing a pet store
5+
version: 1.0.0
6+
contact:
7+
name: API Support
8+
email: support@petstore.example.com
9+
10+
servers:
11+
- url: https://api.petstore.example.com/v1
12+
description: Production server
13+
14+
paths:
15+
/pets:
16+
get:
17+
summary: List all pets
18+
description: Returns a list of all pets in the store
19+
operationId: listPets
20+
tags:
21+
- pets
22+
parameters:
23+
- name: limit
24+
in: query
25+
description: Maximum number of pets to return
26+
required: false
27+
schema:
28+
type: integer
29+
minimum: 1
30+
maximum: 100
31+
default: 20
32+
responses:
33+
'200':
34+
description: Successful response
35+
content:
36+
application/json:
37+
schema:
38+
type: array
39+
items:
40+
$ref: '#/components/schemas/Pet'
41+
'500':
42+
description: Internal server error
43+
content:
44+
application/json:
45+
schema:
46+
$ref: '#/components/schemas/Error'
47+
48+
post:
49+
summary: Create a pet
50+
description: Add a new pet to the store
51+
operationId: createPet
52+
tags:
53+
- pets
54+
requestBody:
55+
required: true
56+
content:
57+
application/json:
58+
schema:
59+
$ref: '#/components/schemas/NewPet'
60+
responses:
61+
'201':
62+
description: Pet created successfully
63+
content:
64+
application/json:
65+
schema:
66+
$ref: '#/components/schemas/Pet'
67+
'400':
68+
description: Invalid input
69+
content:
70+
application/json:
71+
schema:
72+
$ref: '#/components/schemas/Error'
73+
'500':
74+
description: Internal server error
75+
content:
76+
application/json:
77+
schema:
78+
$ref: '#/components/schemas/Error'
79+
80+
/pets/{petId}:
81+
get:
82+
summary: Get a pet by ID
83+
description: Returns a single pet by its ID
84+
operationId: getPetById
85+
tags:
86+
- pets
87+
parameters:
88+
- name: petId
89+
in: path
90+
required: true
91+
description: ID of the pet to retrieve
92+
schema:
93+
type: string
94+
format: uuid
95+
responses:
96+
'200':
97+
description: Successful response
98+
content:
99+
application/json:
100+
schema:
101+
$ref: '#/components/schemas/Pet'
102+
'404':
103+
description: Pet not found
104+
content:
105+
application/json:
106+
schema:
107+
$ref: '#/components/schemas/Error'
108+
'500':
109+
description: Internal server error
110+
content:
111+
application/json:
112+
schema:
113+
$ref: '#/components/schemas/Error'
114+
115+
delete:
116+
summary: Delete a pet
117+
description: Remove a pet from the store
118+
operationId: deletePet
119+
tags:
120+
- pets
121+
parameters:
122+
- name: petId
123+
in: path
124+
required: true
125+
description: ID of the pet to delete
126+
schema:
127+
type: string
128+
format: uuid
129+
responses:
130+
'204':
131+
description: Pet deleted successfully
132+
'404':
133+
description: Pet not found
134+
content:
135+
application/json:
136+
schema:
137+
$ref: '#/components/schemas/Error'
138+
'500':
139+
description: Internal server error
140+
content:
141+
application/json:
142+
schema:
143+
$ref: '#/components/schemas/Error'
144+
145+
components:
146+
schemas:
147+
Pet:
148+
type: object
149+
required:
150+
- id
151+
- name
152+
- species
153+
- status
154+
properties:
155+
id:
156+
type: string
157+
format: uuid
158+
description: Unique identifier for the pet
159+
name:
160+
type: string
161+
minLength: 1
162+
maxLength: 100
163+
description: Name of the pet
164+
species:
165+
type: string
166+
enum:
167+
- dog
168+
- cat
169+
- bird
170+
- rabbit
171+
description: Species of the pet
172+
breed:
173+
type: string
174+
maxLength: 50
175+
description: Breed of the pet (optional)
176+
age:
177+
type: integer
178+
minimum: 0
179+
maximum: 50
180+
description: Age of the pet in years (optional)
181+
status:
182+
type: string
183+
enum:
184+
- available
185+
- pending
186+
- sold
187+
description: Availability status of the pet
188+
createdAt:
189+
type: string
190+
format: date-time
191+
description: Timestamp when the pet was added
192+
193+
NewPet:
194+
type: object
195+
required:
196+
- name
197+
- species
198+
properties:
199+
name:
200+
type: string
201+
minLength: 1
202+
maxLength: 100
203+
description: Name of the pet
204+
species:
205+
type: string
206+
enum:
207+
- dog
208+
- cat
209+
- bird
210+
- rabbit
211+
description: Species of the pet
212+
breed:
213+
type: string
214+
maxLength: 50
215+
description: Breed of the pet (optional)
216+
age:
217+
type: integer
218+
minimum: 0
219+
maximum: 50
220+
description: Age of the pet in years (optional)
221+
222+
Error:
223+
type: object
224+
required:
225+
- code
226+
- message
227+
properties:
228+
code:
229+
type: string
230+
description: Error code
231+
message:
232+
type: string
233+
description: Human-readable error message
234+
details:
235+
type: object
236+
description: Additional error details (optional)

.contractual/versions.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"petstore": {
3+
"version": "0.0.0",
4+
"released": "2026-03-27T16:57:53.638Z"
5+
}
6+
}

.github/workflows/pr-check.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Contract PR Check
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'specs/**'
7+
- '.contractual/**'
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
check:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: '20'
26+
27+
- name: Lint contracts
28+
run: npx @contractual/cli@latest lint
29+
continue-on-error: false
30+
31+
- name: Check for breaking changes
32+
id: breaking
33+
run: |
34+
npx @contractual/cli@latest breaking --format json > breaking.json
35+
cat breaking.json
36+
if jq -e '.hasBreaking == true' breaking.json > /dev/null; then
37+
echo "has_breaking=true" >> $GITHUB_OUTPUT
38+
else
39+
echo "has_breaking=false" >> $GITHUB_OUTPUT
40+
fi
41+
continue-on-error: true
42+
43+
- name: Ensure changeset exists
44+
run: |
45+
# Check if there are any spec changes
46+
HAS_CHANGES=$(npx @contractual/cli@latest diff --format json | jq -e '.contracts | to_entries | map(select(.value.changes | length > 0)) | length > 0')
47+
48+
if [ "$HAS_CHANGES" = "true" ]; then
49+
# Check if changeset exists
50+
if [ -z "$(find .contractual/changesets -name '*.md' -not -name '.gitkeep' 2>/dev/null)" ]; then
51+
echo "::error::Spec changes detected but no changeset found."
52+
echo "::error::Run 'npx @contractual/cli changeset' locally to create one."
53+
exit 1
54+
else
55+
echo "✓ Changeset found"
56+
fi
57+
else
58+
echo "No spec changes detected"
59+
fi
60+
61+
- name: Show diff summary
62+
if: always()
63+
run: npx @contractual/cli@latest diff

.github/workflows/release.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Version Contracts
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- '.contractual/changesets/**'
9+
- '!.contractual/changesets/.gitkeep'
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
jobs:
16+
version:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
token: ${{ secrets.GITHUB_TOKEN }}
24+
25+
- name: Setup Node.js
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: '20'
29+
30+
- name: Configure Git
31+
run: |
32+
git config user.name "github-actions[bot]"
33+
git config user.email "github-actions[bot]@users.noreply.github.com"
34+
35+
- name: Check for changesets
36+
id: check
37+
run: |
38+
if [ -z "$(find .contractual/changesets -name '*.md' -not -name '.gitkeep' 2>/dev/null)" ]; then
39+
echo "No changesets to consume"
40+
echo "has_changesets=false" >> $GITHUB_OUTPUT
41+
else
42+
echo "has_changesets=true" >> $GITHUB_OUTPUT
43+
fi
44+
45+
- name: Apply version bumps
46+
if: steps.check.outputs.has_changesets == 'true'
47+
run: npx @contractual/cli@latest version --yes
48+
49+
- name: Commit and push
50+
if: steps.check.outputs.has_changesets == 'true'
51+
run: |
52+
git add -A
53+
if git diff --staged --quiet; then
54+
echo "No changes to commit"
55+
else
56+
git commit -m "chore: version contracts [skip ci]"
57+
git push origin main
58+
fi

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
.DS_Store
3+
*.log
4+
.env
5+
.env.local

0 commit comments

Comments
 (0)