Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: CI

on:
pull_request:
push:
branches:
- main

jobs:
validate:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Install Spectral CLI
run: npm install -g @stoplight/spectral-cli@6.15.1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ci.yml:28 — global Spectral install contradicts tsp-to-local migration

This PR removes the global tsp install in favor of node_modules/.bin/tsp to eliminate version mismatches, but installs Spectral globally (npm install -g @stoplight/spectral-cli@6.15.1). Consider adding @stoplight/spectral-cli as a pinned devDependency and invoking via npx spectral or node_modules/.bin/spectral for consistency.


- name: Build all schemas
run: |
./build-schema.sh core
./build-schema.sh core --swagger
./build-schema.sh gcp
./build-schema.sh gcp --swagger

- name: Check schema consistency
run: |
if ! git diff --exit-code schemas/; then
echo "Committed schemas are out of sync with TypeSpec sources."
echo "Run './build-schema.sh core --swagger && ./build-schema.sh gcp --swagger' and commit the results."
exit 1
fi
Comment on lines +37 to +43
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this run always regardless of finding schemas in the git diff?


- name: Lint OpenAPI schemas
run: |
spectral lint schemas/core/openapi.yaml schemas/gcp/openapi.yaml --format github-actions

- name: Check version bump
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CURRENT=$(grep -oP '(?<=version: ")[^"]+' main.tsp)
if [ -z "$CURRENT" ]; then
echo "::error::Failed to extract version from main.tsp — check the @info decorator format" >&2
exit 1
fi
LATEST=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null | sed 's/^v//' || echo "")
if [ -z "$LATEST" ]; then
echo "No previous releases found — version check skipped"
exit 0
fi
HIGHEST=$(printf '%s\n%s\n' "$CURRENT" "$LATEST" | sort -V | tail -1)
if [ "$CURRENT" = "$LATEST" ]; then
echo "::error::Version '$CURRENT' matches latest release tag 'v$LATEST' — bump the version in main.tsp before merging."
exit 1
elif [ "$HIGHEST" != "$CURRENT" ]; then
echo "::error::Version '$CURRENT' is lower than latest release 'v$LATEST' — version in main.tsp must be strictly greater."
exit 1
fi
echo "Version bump OK: $LATEST → $CURRENT"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
73 changes: 57 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,91 @@ name: Create Release

on:
push:
tags:
- 'v*'
branches:
- main

jobs:
release:
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: '20'

- name: Install dependencies
run: npm install
run: npm ci

- name: Install tsp
run: npm install -g @typespec/compiler@1.6
- name: Extract version
id: version
run: |
VERSION=$(grep -oP '(?<=version: ")[^"]+' main.tsp)
if [ -z "$VERSION" ]; then
echo "::error::Failed to extract version from main.tsp — check the @info decorator format" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Build Core schema
run: ./build-schema.sh core
- name: Check if release already exists
id: check_tag
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
git fetch --tags
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists — skipping release"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Build GCP schema
run: ./build-schema.sh gcp
- name: Build all schemas
if: steps.check_tag.outputs.skip == 'false'
run: |
./build-schema.sh core
./build-schema.sh core --swagger
./build-schema.sh gcp
./build-schema.sh gcp --swagger

- name: Prepare release assets
if: steps.check_tag.outputs.skip == 'false'
run: |
cp schemas/core/openapi.yaml core-openapi.yaml
cp schemas/core/swagger.yaml core-swagger.yaml
cp schemas/gcp/openapi.yaml gcp-openapi.yaml
cp schemas/gcp/swagger.yaml gcp-swagger.yaml

- name: Create Release
- name: Create release tag
if: steps.check_tag.outputs.skip == 'false'
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"

- name: Create GitHub Release
if: steps.check_tag.outputs.skip == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
generate_release_notes: true
draft: false
prerelease: false
files: |
core-openapi.yaml
core-swagger.yaml
gcp-openapi.yaml
draft: false
prerelease: false
generate_release_notes: true
gcp-swagger.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .spectral.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extends: ["spectral:oas"]
19 changes: 14 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.0.11] - 2026-05-07

### Added

- CONTRIBUTING.md with development guidelines and workflow
- CHANGELOG.md following Keep a Changelog format
- CLAUDE.md with AI agent context and validation workflow
- CI workflow (`ci.yml`) that runs on every PR and push to main: rebuilds all schemas, checks consistency against committed files, lints with `spectral:oas` ruleset, and enforces version bump against latest release tag
- Go module (`go.mod` + `schemas/schemas.go`) exposing all four generated schemas via `//go:embed` as `embed.FS`, enabling downstream consumers to import versioned schemas as a Go module dependency
- `.spectral.yaml` with `spectral:oas` ruleset for OpenAPI 3.0 linting

### Changed

- Improved README.md structure to align with HyperFleet documentation standards
- Release workflow now triggers automatically on push to main instead of requiring a manual tag push; auto-creates annotated tag from version in `main.tsp` and attaches all four schema artifacts (`core-openapi.yaml`, `core-swagger.yaml`, `gcp-openapi.yaml`, `gcp-swagger.yaml`)
- Bumped `actions/checkout` and `actions/setup-node` to v6

### Fixed

- `Error.instance` field format changed from `uri` to `uri-reference` per RFC 9457 (instance identifies a specific occurrence and may be a relative URI reference)
- `build-schema.sh` now resolves `tsp` from `node_modules/.bin/` instead of requiring a global install, eliminating version mismatch between the globally installed compiler and the lockfile-pinned version

## [1.0.10] - 2026-05-05

Expand Down Expand Up @@ -96,7 +104,8 @@ First official stable release of the HyperFleet API specification.
- Interactive API documentation

<!-- Links -->
[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.10...HEAD
[Unreleased]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.11...HEAD
[1.0.11]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.10...v1.0.11
[1.0.10]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.9...v1.0.10
[1.0.9]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.8...v1.0.9
[1.0.8]: https://github.com/openshift-hyperfleet/hyperfleet-api-spec/compare/v1.0.7...v1.0.8
Expand Down
36 changes: 14 additions & 22 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ services/
4. `./build-schema.sh core --swagger`
- Test both provider variants when modifying shared models
- Keep TypeSpec files focused (one resource per service file)
- Use semantic versioning for releases (see RELEASING.md)
- Use semantic versioning for releases (automated on merge to main)

## Common Tasks

Expand Down Expand Up @@ -199,6 +199,7 @@ Before submitting changes:
- [ ] Core Swagger builds: `./build-schema.sh core --swagger`
- [ ] Schema files generated: `ls schemas/*/openapi.yaml`
- [ ] No TypeSpec compilation errors (check output)
- [ ] Schemas pass linting: `spectral lint schemas/core/openapi.yaml schemas/gcp/openapi.yaml`
- [ ] Changes committed including schema updates
- [ ] PR description references related issue

Expand All @@ -207,7 +208,7 @@ Before submitting changes:
**The build-schema.sh script:**
1. Validates provider parameter (core, gcp, etc.)
2. Re-links `aliases.tsp` → `aliases-{provider}.tsp`
3. Runs `tsp compile main.tsp`
3. Runs `node_modules/.bin/tsp compile main.tsp`
4. Copies output to `schemas/{provider}/openapi.yaml`
5. (Optional) Converts to OpenAPI 2.0 with `--swagger` flag

Expand Down Expand Up @@ -242,27 +243,18 @@ Match the version range to existing dependencies.

## Release Process

Quick reference (see RELEASING.md for details):
Releases are **fully automated** via GitHub Actions (`.github/workflows/release.yml`).

```bash
# 1. Build schemas
npm run build:all

# 2. Commit and tag
git add schemas/
git commit -m "chore: update schemas for vX.Y.Z"
git tag -a vX.Y.Z -m "Release vX.Y.Z"

# 3. Push tag
git push upstream vX.Y.Z

# 4. Create GitHub Release with schema assets
gh release create vX.Y.Z \
--repo openshift-hyperfleet/hyperfleet-api-spec \
--title "vX.Y.Z" \
schemas/core/openapi.yaml#core-openapi.yaml \
schemas/gcp/openapi.yaml#gcp-openapi.yaml
```
On every push to `main`, the release workflow:
1. Extracts the version from the `@info` decorator in `main.tsp`
2. Skips if a tag for that version already exists
3. Builds all four schema variants (core/gcp OpenAPI 3.0 + Swagger 2.0)
4. Creates an annotated Git tag (`vX.Y.Z`)
5. Publishes a GitHub Release with all four artifacts attached

The CI workflow (`.github/workflows/ci.yml`) enforces that the version in `main.tsp` is bumped from the latest release tag before a PR can be merged.

To release a new version, simply bump the version in `main.tsp` and merge to `main`.

## Architecture Context

Expand Down
40 changes: 25 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,13 @@ Thank you for your interest in contributing to the HyperFleet API specification!
cd hyperfleet-api-spec
```

2. Install TypeSpec compiler globally:

```bash
npm install -g @typespec/compiler
```

3. Install project dependencies:
2. Install project dependencies (includes the TypeSpec compiler locally):

```bash
npm install
```

4. Verify your setup by building the schemas:
3. Verify your setup by building the schemas:

```bash
npm run build:core
Expand Down Expand Up @@ -85,6 +79,22 @@ npm run build:gcp:swagger
npm run build:all
```

### Linting Schemas

CI automatically lints OpenAPI schemas using a pinned version of [Spectral](https://github.com/stoplightio/spectral) installed locally in the workflow. For local linting during development, install Spectral globally:

```bash
npm install -g @stoplight/spectral-cli
```

Then lint the generated schemas:

```bash
spectral lint schemas/core/openapi.yaml schemas/gcp/openapi.yaml
```

The `.spectral.yaml` config at the repo root applies the `spectral:oas` ruleset.

### Validating Output

After building, verify the generated schemas:
Expand Down Expand Up @@ -212,15 +222,15 @@ refactor: consolidate common status fields

## Release Process

See [RELEASING.md](RELEASING.md) for detailed release instructions.
Releases are **fully automated**. See [RELEASING.md](RELEASING.md) for details.

When a PR is merged to `main`, the release workflow automatically:

**Quick summary:**
1. Extracts the version from `main.tsp`
2. Creates an annotated Git tag
3. Publishes a GitHub Release with all four schema artifacts attached

1. Build schemas: `npm run build:all`
2. Commit changes
3. Create tag: `git tag -a vX.Y.Z -m "Release vX.Y.Z"`
4. Push tag: `git push upstream vX.Y.Z`
5. Create GitHub Release with `core-openapi.yaml` and `gcp-openapi.yaml` assets
The CI workflow enforces that the version in `main.tsp` is bumped from the latest release tag before a PR can be merged.

## Pull Request Process

Expand Down
Loading