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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Generated files - exclude from language stats and diffs
client/dashboard/src/sdk/** linguist-generated
client/admin/src/sdk/** linguist-generated -whitespace
server/gen/** linguist-generated
server/internal/**/*.sql.go linguist-generated
openrouter/** linguist-generated
.speakeasy/*.lock linguist-generated
.speakeasy/out.openapi.yaml linguist-generated
.speakeasy/out.admin.openapi.yaml linguist-generated
**/descriptors.pb linguist-generated
14 changes: 14 additions & 0 deletions .github/filters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,30 @@ tsframework:
- "ts-framework/**"

sdk:
- "client/admin/src/sdk/**"
- "client/dashboard/src/sdk/**"
- "package.json"
- "pnpm-lock.yaml"

sdk-gen:
- ".mise-tasks/gen/**"
- ".mise-tasks/test/gen-resolve.sh"
- ".mise-tasks/test/sdk-overlays.sh"
- ".speakeasy/out.admin.openapi.yaml"
- ".speakeasy/workflow.yaml"
- "client/admin/src/sdk/**"
- "client/dashboard/src/sdk/.speakeasy/gen.yaml"
- "hooks/sdk/.speakeasy/gen.yaml"
- "mise.toml"
- "overlays/**"
- "server/design/**"
- "server/internal/constants/accounts.go"
- "server/internal/constants/auth.go"
- "server/internal/constants/spend_cap.go"
- "server/internal/constants/trials.go"
- "server/internal/conv/from.go"
- "server/internal/oops/codes.go"
- "server/internal/productfeatures/features.go"
- "server/gen/http/openapi3.yaml"

migrations:
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1943,9 +1943,18 @@ jobs:
- name: Install dependencies
run: aube install --frozen-lockfile

- name: Test generated artifact conflict resolution
run: mise run test:gen-resolve

- name: Test SDK overlays
run: mise run test:sdk-overlays

- name: Test SDK generation check
run: mise run test:gen-sdk

- name: Check transformed SDK specs
run: mise run gen:sdk --check

- name: Regenerate SDK
run: mise run gen:sdk

Expand Down
18 changes: 16 additions & 2 deletions .mise-tasks/gen/resolve.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@ if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
exit 1
fi

paths=(.speakeasy client/dashboard/src/sdk server/gen)
paths=(
.speakeasy/openapi-hooks.yaml
.speakeasy/out.admin.openapi.yaml
.speakeasy/out.openapi.yaml
.speakeasy/workflow.lock
client/admin/src/sdk
client/dashboard/src/sdk
server/gen
)

echo "==> Checking out $base for: ${paths[*]}"
git checkout "$base" -- "${paths[@]}"
for path in "${paths[@]}"; do
if git cat-file -e "$base:$path" 2>/dev/null; then
git checkout "$base" -- "$path"
else
echo "==> $path is absent from $base; keeping it for regeneration"
fi
done

echo "==> Regenerating Goa server"
mise run gen:goa-server
Expand Down
47 changes: 34 additions & 13 deletions .mise-tasks/gen/sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#MISE description="Generate SDK from OpenAPI spec"

#USAGE flag "-c --check" help="Check if the Gram-Internal OpenAPI output is up-to-date"
#USAGE flag "-c --check" help="Check if the Gram-Internal and Gram-Admin OpenAPI outputs are up-to-date"

set -e

Expand All @@ -23,18 +23,39 @@ generate() {

check_inputs() {
workflow=".speakeasy/workflow.yaml"
output=$(yq '.sources.Gram-Internal.output' "$workflow")
expected=$(mktemp)
cp "$output" "$expected"
trap 'cp "$expected" "$output"; rm -f "$expected"' EXIT

generate --source Gram-Internal >/dev/null 2>&1

if ! diff -q "$expected" "$output" >/dev/null 2>&1; then
echo "Gram-Internal OpenAPI spec is out of date. Run 'mise gen:sdk' to regenerate." >&2
exit 1
fi
echo "Gram-Internal OpenAPI spec is up to date."
tmpdir=$(mktemp -d)
sources=(Gram-Internal Gram-Admin)
outputs=()

for source in "${sources[@]}"; do
output=$(yq ".sources[\"${source}\"].output" "$workflow")
outputs+=("$output")
cp "$output" "$tmpdir/$source.yaml"
done

restore_inputs() {
for i in "${!sources[@]}"; do
cp "$tmpdir/${sources[$i]}.yaml" "${outputs[$i]}"
done
rm -rf "$tmpdir"
}
trap restore_inputs EXIT

status=0
for i in "${!sources[@]}"; do
source=${sources[$i]}
output=${outputs[$i]}
generate --source "$source" >/dev/null 2>&1

if ! diff -q "$tmpdir/$source.yaml" "$output" >/dev/null 2>&1; then
echo "${source} OpenAPI spec is out of date. Run 'mise gen:sdk' to regenerate." >&2
status=1
else
echo "${source} OpenAPI spec is up to date."
fi
done

return "$status"
}

if [[ "${usage_check:-}" == "true" ]]; then
Expand Down
89 changes: 89 additions & 0 deletions .mise-tasks/test/gen-resolve.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env bash

#MISE description="Test generated artifact conflict resolution with a base missing a target"

set -euo pipefail

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
repo="$tmpdir/repo"
mkdir -p "$repo" "$tmpdir/bin"
cp .mise-tasks/gen/resolve.sh "$repo/resolve.sh"

cd "$repo"
git init -q -b main
git config user.email test@example.invalid
git config user.name "Resolve Test"
mkdir -p .speakeasy client/dashboard/src/sdk server/gen
cat >.speakeasy/workflow.yaml <<'EOF'
sources:
Gram-Internal:
output: .speakeasy/out.openapi.yaml
targets:
gram-internal:
output: client/dashboard/src/sdk
EOF
printf 'base\n' >.speakeasy/openapi-hooks.yaml
printf 'base\n' >.speakeasy/out.openapi.yaml
printf 'base\n' >.speakeasy/workflow.lock
printf 'base\n' >client/dashboard/src/sdk/marker
printf 'base\n' >server/gen/marker
git add .
git commit -qm base

git switch -qc feature
cat >.speakeasy/workflow.yaml <<'EOF'
sources:
Gram-Internal:
output: .speakeasy/out.openapi.yaml
Gram-Admin:
output: .speakeasy/out.admin.openapi.yaml
targets:
gram-internal:
output: client/dashboard/src/sdk
gram-admin:
output: client/admin/src/sdk
EOF
printf 'feature\n' >.speakeasy/openapi-hooks.yaml
printf 'feature\n' >.speakeasy/out.openapi.yaml
printf 'feature\n' >.speakeasy/out.admin.openapi.yaml
printf 'feature\n' >.speakeasy/workflow.lock
printf 'feature\n' >client/dashboard/src/sdk/marker
printf 'feature\n' >server/gen/marker
mkdir -p client/admin/src/sdk
printf 'feature\n' >client/admin/src/sdk/marker
git add .
git commit -qm feature

cat >"$tmpdir/bin/mise" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
case "$*" in
"run gen:goa-server") touch server/gen/goa-regenerated ;;
"run gen:sdk")
test "$(cat .speakeasy/openapi-hooks.yaml)" = base
test "$(cat .speakeasy/out.openapi.yaml)" = base
test "$(cat .speakeasy/out.admin.openapi.yaml)" = feature
test "$(cat .speakeasy/workflow.lock)" = base
test "$(cat client/admin/src/sdk/marker)" = feature
mkdir -p client/dashboard/src/sdk
touch client/dashboard/src/sdk/sdk-regenerated
admin_output=$(awk '/^ gram-admin:/{ target = 1; next } target && /output:/{ print $2; exit }' .speakeasy/workflow.yaml)
test "$admin_output" = client/admin/src/sdk
mkdir -p "$admin_output"
touch "$admin_output/sdk-regenerated"
;;
*) echo "unexpected mise invocation: $*" >&2; exit 1 ;;
esac
EOF
chmod +x "$tmpdir/bin/mise"
PATH="$tmpdir/bin:$PATH" bash ./resolve.sh

for marker in .speakeasy/openapi-hooks.yaml .speakeasy/out.openapi.yaml .speakeasy/workflow.lock client/dashboard/src/sdk/marker server/gen/marker; do
test "$(cat "$marker")" = base
done
grep -q '^ gram-admin:' .speakeasy/workflow.yaml
test -e server/gen/goa-regenerated
test -e client/dashboard/src/sdk/sdk-regenerated
test -e client/admin/src/sdk/sdk-regenerated
test "$(cat client/admin/src/sdk/marker)" = feature
20 changes: 18 additions & 2 deletions .mise-tasks/test/gen-sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,21 @@ sources:
transformations:
- removeUnused: true
output: internal-output.yaml
Gram-Admin:
inputs:
- location: admin.yaml
overlays:
- location: admin-overlay.yaml
transformations:
- removeUnused: true
output: admin-output.yaml
EOF
printf 'internal\n' >internal.yaml
printf 'admin\n' >admin.yaml
printf 'overlay\n' >internal-overlay.yaml
printf 'overlay\n' >admin-overlay.yaml
printf 'internal\n' >internal-output.yaml
printf 'admin\n' >admin-output.yaml

cat >"$tmpdir/bin/speakeasy" <<'EOF'
#!/usr/bin/env bash
Expand All @@ -39,8 +50,11 @@ if [[ "$1" == "run" ]]; then
fi
shift
done
test "$source" = Gram-Internal
cp internal.yaml internal-output.yaml
case "$source" in
Gram-Internal) cp internal.yaml internal-output.yaml ;;
Gram-Admin) cp admin.yaml admin-output.yaml ;;
*) exit 1 ;;
esac
exit
fi

Expand All @@ -52,7 +66,9 @@ chmod +x "$tmpdir/bin/speakeasy"
export SPEAKEASY_LOG="$tmpdir/speakeasy.log"
PATH="$tmpdir/bin:$PATH" usage_check=true bash ./sdk.sh
grep -q '^run .*--source Gram-Internal' "$SPEAKEASY_LOG"
grep -q '^run .*--source Gram-Admin' "$SPEAKEASY_LOG"
test "$(cat internal-output.yaml)" = internal
test "$(cat admin-output.yaml)" = admin

printf 'changed\n' >internal.yaml
if PATH="$tmpdir/bin:$PATH" usage_check=true bash ./sdk.sh >/dev/null 2>&1; then
Expand Down
31 changes: 30 additions & 1 deletion .mise-tasks/test/sdk-overlays.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env bash

#MISE description="Prove Admin operations cannot affect the Dashboard SDK input"
#MISE description="Prove SDK overlays isolate and name Admin operations"

set -euo pipefail

raw_spec="server/gen/http/openapi3.yaml"
common_overlay="overlays/goa-common.yaml"
dashboard_overlay="overlays/dashboard-sdk.yaml"
admin_overlay="overlays/admin-sdk.yaml"
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

Expand Down Expand Up @@ -59,3 +60,31 @@ if grep -q 'admin_auth_header_Authorization' "$output"; then
echo "Dashboard input contains Admin auth" >&2
exit 1
fi

speakeasy overlay apply \
--schema "$tmpdir/baseline.common.yaml" \
--overlay "$admin_overlay" \
--out "$tmpdir/admin.yaml" >/dev/null 2>&1

operation_rows=$(
yq -o=json "$tmpdir/admin.yaml" |
jq -r '.paths[][] | select(.operationId and (."x-speakeasy-ignore" != true)) | [.operationId, (."x-speakeasy-name-override" // "")] | @tsv'
)
if [ -z "$operation_rows" ]; then
echo "Expected active Admin operations, found none" >&2
exit 1
fi

while IFS=$'\t' read -r operation_id sdk_name; do
Comment thread
walker-tx marked this conversation as resolved.
if [[ $operation_id != admin* ]]; then
echo "Expected stable admin-prefixed operation ID, found $operation_id" >&2
exit 1
fi
local_name=${operation_id#admin}
first_letter=$(printf '%s' "${local_name:0:1}" | tr '[:upper:]' '[:lower:]')
expected_name="$first_letter${local_name:1}"
if [ "$sdk_name" != "$expected_name" ]; then
echo "Expected $operation_id to have SDK name $expected_name, found ${sdk_name:-none}" >&2
exit 1
fi
done <<<"$operation_rows"
1 change: 1 addition & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"**/*.mdx",
"/.speakeasy/**",
"/client/admin/src/routeTree.gen.ts",
"/client/admin/src/sdk/**",
"/client/dashboard/index.html",
"/client/dashboard/public/external/**",
"/client/dashboard/src/App.css",
Expand Down
Loading
Loading