Skip to content
Merged
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
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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: 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 thread
rh-amarin marked this conversation as resolved.

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

- 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
Comment on lines +55 to +58
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat GitHub API failures as “no previous releases.”

LATEST=$(... || echo "") collapses real gh failures into an empty value, and Line 56-58 then skips version enforcement. That can let non-bumped versions merge when the API call fails transiently.

Suggested fix
-          LATEST=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null | sed 's/^v//' || echo "")
-          if [ -z "$LATEST" ]; then
+          if ! RAW_LATEST=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null); then
+            echo "::error::Failed to query latest GitHub release tag via gh."
+            exit 1
+          fi
+          LATEST=$(printf '%s' "$RAW_LATEST" | sed 's/^v//')
+          if [ -z "$RAW_LATEST" ]; then
             echo "No previous releases found — version check skipped"
             exit 0
           fi

As per coding guidelines, "CI requirements for PRs: Enforce a strict version bump ... and fail unless main.tsp’s version is strictly greater than the latest release (or skip if no prior releases)."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
CURRENT=$(grep -oP '(?<=version: ")[^"]+' main.tsp)
if [ -z "$CURRENT" ]; then
echo "Could not extract version from main.tsp — check the \`@info\` decorator format."
exit 1
fi
if ! RAW_LATEST=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null); then
echo "::error::Failed to query latest GitHub release tag via gh."
exit 1
fi
LATEST=$(printf '%s' "$RAW_LATEST" | sed 's/^v//')
if [ -z "$RAW_LATEST" ]; then
echo "No previous releases found — version check skipped"
exit 0
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 55 - 58, The current command collapses
real gh failures into an empty LATEST and silently skips version checks; change
the logic around the gh release list call so you capture its output and exit
status separately: run gh release list --limit 1 --json tagName --jq
'.[0].tagName' and store its stdout into a temp var, check the command's exit
code and if non-zero fail the CI with a clear error (do not fall back to empty),
only treat an empty stdout as "no previous releases" (then echo "No previous
releases found — version check skipped" and exit 0), and when stdout is
non-empty set LATEST by stripping a leading v (current LATEST variable) before
doing the version comparison.

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 }}
7 changes: 7 additions & 0 deletions .spectral.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extends: ["spectral:oas"]
overrides:
- files:
- "schemas/core/openapi.yaml#/components/schemas/BearerAuth"
- "schemas/gcp/openapi.yaml#/components/schemas/BearerAuth"
rules:
oas3-unused-component: off
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,28 @@ 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

- 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
- Renamed aggregated condition `Available` to `LastKnownReconciled` in cluster and nodepool status conditions (HYPERFLEET-1017)
- Updated condition examples and descriptions to reflect `LastKnownReconciled` semantics
- Fixed typo `Avaliable` → `Available` in adapter example constants (HYPERFLEET-971)
- Improved README.md structure to align with HyperFleet documentation standards

### 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

### Added
Expand Down Expand Up @@ -99,7 +108,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
```
Comment on lines +84 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use local/pinned Spectral in contributor instructions, not a global install

Line 84 correctly calls out pinned CI behavior, but Lines 85-94 switch to a global install and unpinned binary invocation. That can produce lint results different from CI and hurts reproducibility for contributors.

Suggested doc fix
-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:
+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, use the project-local dependency:

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

Then lint the generated schemas:

-spectral lint schemas/core/openapi.yaml schemas/gcp/openapi.yaml
+node_modules/.bin/spectral lint schemas/core/openapi.yaml schemas/gcp/openapi.yaml
</details>

As per coding guidelines, “Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity. Validate changes against HyperFleet architecture standards from the linked architecture repository.”

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @CONTRIBUTING.md around lines 84 - 94, Update the CONTRIBUTING.md local
linting instructions to use the repository's pinned Spectral instead of a global
install: replace the global install command "npm install -g
@stoplight/spectral-cli" with "npm ci" (or a command that installs pinned
devDependencies) and replace the invocation "spectral lint
schemas/core/openapi.yaml schemas/gcp/openapi.yaml" with the local binary path
"node_modules/.bin/spectral lint schemas/core/openapi.yaml
schemas/gcp/openapi.yaml" so contributors run the exact Spectral version used by
CI.


</details>

<!-- fingerprinting:phantom:triton:hawk -->

<!-- d98c2f50 -->

<!-- This is an auto-generated comment by CodeRabbit -->


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