Skip to content
Draft
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
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CURRENT=$(grep -oP '(?<=version: ")[^"]+' main.tsp)
CURRENT=$(grep -oP '(?<=version: ")[^"]+' core/main.tsp)
if [ -z "$CURRENT" ]; then
echo "::error::Failed to extract version from main.tsp — check the @info decorator format" >&2
echo "::error::Failed to extract version from core/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 "")
Expand All @@ -59,10 +59,10 @@ jobs:
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."
echo "::error::Version '$CURRENT' matches latest release tag 'v$LATEST' — bump the version in core/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."
echo "::error::Version '$CURRENT' is lower than latest release 'v$LATEST' — version in core/main.tsp must be strictly greater."
exit 1
fi
echo "Version bump OK: $LATEST → $CURRENT"
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
- name: Extract version
id: version
run: |
VERSION=$(grep -oP '(?<=version: ")[^"]+' main.tsp)
VERSION=$(grep -oP '(?<=version: ")[^"]+' core/main.tsp)
if [ -z "$VERSION" ]; then
echo "::error::Failed to extract version from main.tsp — check the @info decorator format" >&2
echo "::error::Failed to extract version from core/main.tsp — check the @info decorator format" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
Expand Down
9 changes: 0 additions & 9 deletions aliases-core.tsp

This file was deleted.

8 changes: 0 additions & 8 deletions aliases-gcp.tsp

This file was deleted.

1 change: 0 additions & 1 deletion aliases.tsp

This file was deleted.

46 changes: 14 additions & 32 deletions build-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,29 +58,20 @@ done
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Define the aliases file for the provider
ALIASES_FILE="aliases-${PROVIDER}.tsp"

# Check if the aliases file exists
if [ ! -f "$ALIASES_FILE" ]; then
echo -e "${RED}Error: Provider aliases file not found: ${ALIASES_FILE}${NC}"
# Resolve the provider entry point
PROVIDER_ENTRY="${PROVIDER}/main.tsp"
if [ ! -f "$PROVIDER_ENTRY" ]; then
Comment on lines +62 to +63
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

Validate provider names before using them in filesystem paths.

Line 62 and Line 100 trust raw $PROVIDER in path construction. Inputs containing / or .. can escape intended locations (compile source and output dir). Add an allowlist check before using $PROVIDER.

Suggested fix
 PROVIDER="$1"
 shift
 
+# Restrict provider to simple directory names (no path separators/traversal)
+if [[ ! "$PROVIDER" =~ ^[a-zA-Z0-9_-]+$ ]]; then
+    echo -e "${RED}Error: Invalid provider name: ${PROVIDER}${NC}"
+    echo "Provider must contain only letters, numbers, '-' or '_'"
+    exit 1
+fi
+
 # Check if provider looks like an option (starts with -)
 if [[ "$PROVIDER" == -* ]]; then

Also applies to: 99-101

🤖 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 `@build-schema.sh` around lines 62 - 63, The script uses raw $PROVIDER to build
paths (e.g., PROVIDER_ENTRY="${PROVIDER}/main.tsp") which allows directory
traversal or slashes; add an allowlist validation for PROVIDER before any use
(e.g., validate that PROVIDER matches a safe regex such as only alphanumerics,
hyphen and underscore, and no slashes or dots) and exit with an error if it
fails; apply this validation once near the top of the script and use the
validated variable for all subsequent uses (including where PROVIDER is used to
construct compile source and output directories) so functions/variables like
PROVIDER_ENTRY and any compile/output path constructions never see unvalidated
input.

echo -e "${RED}Error: Provider entry point not found: ${PROVIDER_ENTRY}${NC}"
echo ""
echo "Available providers:"
for file in aliases-*.tsp; do
if [ -f "$file" ]; then
provider_name=$(echo "$file" | sed 's/aliases-\(.*\)\.tsp/\1/')
echo " - $provider_name"
for dir in */; do
if [ -f "${dir}main.tsp" ]; then
echo " - ${dir%/}"
fi
done
exit 1
fi

# Check if main.tsp exists
if [ ! -f "main.tsp" ]; then
echo -e "${RED}Error: main.tsp not found in current directory${NC}"
exit 1
fi

# Check if tsp command is available
if [ ! -x "${SCRIPT_DIR}/node_modules/.bin/tsp" ]; then
echo -e "${RED}Error: tsp not found in node_modules. Run 'npm install' first.${NC}"
Expand All @@ -105,24 +96,15 @@ else
fi
echo ""

# Step 1: Re-link aliases.tsp to the provider-specific aliases file
echo -e "${YELLOW}Step 1: Linking aliases.tsp to ${ALIASES_FILE}${NC}"
if [ -L "aliases.tsp" ] || [ -f "aliases.tsp" ]; then
rm -f aliases.tsp
fi
ln -sf "$ALIASES_FILE" aliases.tsp
echo -e "${GREEN}✓ Linked aliases.tsp → ${ALIASES_FILE}${NC}"
echo ""

# Step 2: Create output directory for the provider
# Step 1: Create output directory for the provider
OUTPUT_DIR="schemas/${PROVIDER}"
echo -e "${YELLOW}Step 2: Preparing output directory...${NC}"
echo -e "${YELLOW}Step 1: Preparing output directory...${NC}"
mkdir -p "$OUTPUT_DIR"
echo -e "${GREEN}✓ Created output directory: ${OUTPUT_DIR}${NC}"
echo ""

# Step 3: Compile TypeSpec to generate OpenAPI schema
echo -e "${YELLOW}Step 3: Compiling TypeSpec...${NC}"
# Step 2: Compile TypeSpec to generate OpenAPI schema
echo -e "${YELLOW}Step 2: Compiling TypeSpec from ${PROVIDER_ENTRY}...${NC}"
TEMP_OUTPUT_DIR="tsp-output-${PROVIDER}"

# Cleanup function to remove temporary directory on exit
Expand All @@ -133,7 +115,7 @@ cleanup() {
}
trap cleanup EXIT

if "$TSP" compile main.tsp --output-dir "$TEMP_OUTPUT_DIR"; then
if "$TSP" compile "$PROVIDER_ENTRY" --output-dir "$TEMP_OUTPUT_DIR"; then
# Move the generated schema to the provider-specific directory
if [ -f "${TEMP_OUTPUT_DIR}/schema/openapi.yaml" ]; then
mv "${TEMP_OUTPUT_DIR}/schema/openapi.yaml" "${OUTPUT_DIR}/openapi.yaml"
Expand All @@ -152,10 +134,10 @@ else
exit 1
fi

# Step 4: Convert to OpenAPI 2.0 (Swagger) if requested
# Step 3: Convert to OpenAPI 2.0 (Swagger) if requested
if [ "$GENERATE_SWAGGER" = true ]; then
echo ""
echo -e "${YELLOW}Step 4: Converting to OpenAPI 2.0 (Swagger)...${NC}"
echo -e "${YELLOW}Step 3: Converting to OpenAPI 2.0 (Swagger)...${NC}"

if npx api-spec-converter \
--from=openapi_3 \
Expand Down
50 changes: 50 additions & 0 deletions core/main.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import "@typespec/http";
import "@typespec/openapi";
import "@typespec/openapi3";

import "./models/cluster/model.tsp";
import "./models/cluster/example_cluster.tsp";
import "./models/cluster/example_post.tsp";
import "./models/cluster/example_patch.tsp";
import "./models/nodepool/model.tsp";
import "./models/nodepool/example_nodepool.tsp";
import "./models/nodepool/example_post.tsp";
import "./models/nodepool/example_patch.tsp";
import "./services/statuses-internal.tsp";

import "../shared/services/clusters.tsp";
import "../shared/services/statuses.tsp";
import "../shared/services/nodepools.tsp";

using Http;
using OpenAPI;

/**
* HyperFleet API provides simple CRUD operations for managing cluster resources and their status history.
*
* **Architecture**: Simple CRUD only, no business logic, no event creation.
* Sentinel operator handles all orchestration logic.
* Adapters handle the specifics of managing spec
*
*/
@service(#{ title: "HyperFleet API" })
@info(#{
version: "1.0.15",
contact: #{
name: "HyperFleet Team",
url: "https://github.com/openshift-hyperfleet",
},
license: #{
name: "Apache 2.0",
url: "https://www.apache.org/licenses/LICENSE-2.0",
},
})
@server("https://hyperfleet.redhat.com", "Production")
@route("/api/hyperfleet/v1")
namespace HyperFleet;

// Override BearerAuth to use lowercase "bearer" as required by kin-openapi
model BearerAuth {
type: AuthType.http;
scheme: "bearer";
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "../../aliases-core.tsp";
import "../../models/clusters/model.tsp";
import "../../models/common/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";
import "../../../shared/models/common/model.tsp";

const exampleCluster: Cluster = #{
kind: "Cluster",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-core.tsp";
import "../../models/clusters/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";

const exampleClusterPatchRequest: ClusterPatchRequest = #{
spec: #{},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-core.tsp";
import "../../models/clusters/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";

const exampleClusterCreateRequest: ClusterCreateRequest = #{
kind: "Cluster",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* Accepts any properties as the spec is provider-agnostic.
* This is represented as a simple object to allow flexibility.
*/
model ClusterSpec {}
model ClusterSpec {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "../../aliases-core.tsp";
import "../../models/nodepools/model.tsp";
import "../../models/common/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";
import "../../../shared/models/common/model.tsp";

const exampleNodePool: NodePool = #{
kind: "NodePool",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-core.tsp";
import "../../models/nodepools/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";

const exampleNodePoolPatchRequest: NodePoolPatchRequest = #{
spec: #{},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-core.tsp";
import "../../models/nodepools/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";

const exampleNodePoolCreateRequest: NodePoolCreateRequest = #{
name: "worker-pool-1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* Accepts any properties as the spec is provider-agnostic.
* This is represented as a simple object to allow flexibility.
*/
model NodePoolSpec {}
model NodePoolSpec {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import "@typespec/http";
import "@typespec/openapi";
import "@typespec/openapi3";

import "../models/statuses/model.tsp";
import "../models/common/model.tsp";
import "../models/nodepools/model.tsp";
import "../../shared/models/statuses/model.tsp";
import "../../shared/models/common/model.tsp";
import "../../shared/models/nodepools/model.tsp";

using Http;
using OpenAPI;
Expand Down
19 changes: 13 additions & 6 deletions main.tsp → gcp/main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import "@typespec/http";
import "@typespec/openapi";
import "@typespec/openapi3";

import "./services/clusters.tsp";
import "./services/statuses.tsp";
import "./services/nodepools.tsp";
// Provider-specific security is imported via aliases.tsp
import "./aliases.tsp";
import "./models/cluster/model.tsp";
import "./models/cluster/example_cluster.tsp";
import "./models/cluster/example_post.tsp";
import "./models/cluster/example_patch.tsp";
import "./models/nodepool/model.tsp";
import "./models/nodepool/example_nodepool.tsp";
import "./models/nodepool/example_post.tsp";
import "./models/nodepool/example_patch.tsp";

import "../shared/services/clusters.tsp";
import "../shared/services/statuses.tsp";
import "../shared/services/nodepools.tsp";

using Http;
using OpenAPI;
Expand All @@ -21,7 +28,7 @@ using OpenAPI;
*/
@service(#{ title: "HyperFleet API" })
@info(#{
version: "1.0.14",
version: "1.0.15",
contact: #{
name: "HyperFleet Team",
url: "https://github.com/openshift-hyperfleet",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "../../aliases-gcp.tsp";
import "../../models/clusters/model.tsp";
import "../../models/common/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";
import "../../../shared/models/common/model.tsp";

const exampleCluster: Cluster = #{
kind: "Cluster",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-gcp.tsp";
import "../../models/clusters/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";

const exampleClusterPatchRequest: ClusterPatchRequest = #{
spec: #{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-gcp.tsp";
import "../../models/clusters/model.tsp";
import "./model.tsp";
import "../../../shared/models/clusters/model.tsp";

const exampleClusterCreateRequest: ClusterCreateRequest = #{
kind: "Cluster",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "../../aliases-gcp.tsp";
import "../../models/nodepools/model.tsp";
import "../../models/common/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";
import "../../../shared/models/common/model.tsp";

const exampleNodePool: NodePool = #{
kind: "NodePool",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-gcp.tsp";
import "../../models/nodepools/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";

const exampleNodePoolPatchRequest: NodePoolPatchRequest = #{
spec: #{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "../../aliases-gcp.tsp";
import "../../models/nodepools/model.tsp";
import "./model.tsp";
import "../../../shared/models/nodepools/model.tsp";
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const exampleNodePoolCreateRequest: NodePoolCreateRequest = #{
name: "worker-pool-1",
Expand Down
File renamed without changes.
6 changes: 0 additions & 6 deletions models/compatibility/README.md

This file was deleted.

2 changes: 1 addition & 1 deletion schemas/core/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ info:
Sentinel operator handles all orchestration logic.
Adapters handle the specifics of managing spec
tags:
- name: Clusters
- name: Cluster statuses
- name: NodePool statuses
- name: Clusters
- name: NodePools
paths:
/api/hyperfleet/v1/clusters:
Expand Down
2 changes: 1 addition & 1 deletion schemas/core/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1866,9 +1866,9 @@ securityDefinitions:
name: Authorization
type: apiKey
tags:
- name: Clusters
- name: Cluster statuses
- name: NodePool statuses
- name: Clusters
- name: NodePools
x-components:
parameters:
Expand Down
Loading
Loading