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
17 changes: 9 additions & 8 deletions docker/scripts/app-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ echo "Populating initial history..."
# Collect statics
./manage.py collectstatic --no-input || true

# Django should be operational now. Build purple API client.
./manage.py spectacular --file purple_api.yaml && \
npx --yes @openapitools/openapi-generator-cli@2.29 generate --generator-key purple || true
# If not set, add @ts-nocheck in runtime.ts to avoid type errors from generated code
if ! grep -q "// @ts-nocheck" "client/app/purple_client/runtime.ts"; then
sed -i '1i // @ts-nocheck' "client/app/purple_client/runtime.ts"
# Django should be operational now. Build the purple API client from the
# current code (schema -> client, with an in-sync check). The generated client
# is gitignored and must be rebuilt on every container init. Failure here is
# non-fatal to startup but surfaces loudly: a stale client makes the frontend
# throw "api.<something> is not a function". Recover by re-running the same
# script once the backend is healthy.
if ! bash docker/scripts/build-purple-client.sh; then
echo "WARNING: purple client build failed or is out of sync." >&2
echo " Once Django is healthy, run: docker/scripts/build-purple-client.sh" >&2
fi
/usr/bin/mkdir -p client/app/purple_client
/bin/cp purple_api.yaml client/app/purple_client/.purple_api.yaml

# Install client dependencies
cd client
Expand Down
55 changes: 55 additions & 0 deletions docker/scripts/build-purple-client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This script is largely redundant with the existing update-rpcapi script. That other script also generates the Python client used to interact with datatracker. Breaking that into separate scripts is not a bad choice, but it should be updated to reduce redundancy if we do that.

The update-rpcapi does handle the situation that a datatracker is not available and will still generate the purple API client even in that case.

#
# Regenerate the purple OpenAPI schema (purple_api.yaml) and the generated
# TypeScript client (client/app/purple_client/) from the CURRENT Django code.
#
# The generated client is gitignored, so it does NOT survive a container
# rebuild and must be rebuilt from the running code. This is also the recovery
# path whenever the frontend reports "api.<something> is not a function": that
# error means the generated client is out of sync with the backend. Safe to
# re-run any time (e.g. after a rebuild or a branch switch).
Comment on lines +6 to +10

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this is accurate if you're mounting the workspace in the dev container. The generated client will be written into your working directory, where it will persist even though it's gitignored. Tearing down the container won't delete it.

The generated client would not be present on a clean checkout, though. The comment would also be true if you're using a workspace entirely inside a docker volume.


set -o pipefail
cd /workspace || exit 1

echo "Regenerating purple API schema from Django..."
if ! ./manage.py spectacular --file purple_api.yaml; then
echo "ERROR: 'manage.py spectacular' failed — purple_api.yaml NOT updated." >&2
echo " Refusing to rebuild the client from a stale schema. Fix the" >&2
echo " backend, then re-run docker/scripts/build-purple-client.sh." >&2
exit 1
fi

echo "Generating purple TypeScript client..."
if ! npx --yes @openapitools/openapi-generator-cli@2.29 generate --generator-key purple; then
echo "ERROR: openapi-generator failed — client/app/purple_client may be stale." >&2
exit 1
fi

# The generator strips it; @ts-nocheck avoids type errors from generated code.
if ! grep -q "// @ts-nocheck" client/app/purple_client/runtime.ts; then
sed -i '1i // @ts-nocheck' client/app/purple_client/runtime.ts
fi

# Keep a copy of the exact schema the client was built from.
mkdir -p client/app/purple_client
cp purple_api.yaml client/app/purple_client/.purple_api.yaml

# Sanity check: every operationId in the schema should have a matching method
# in the client (schema is snake_case, client methods are camelCase). This is
# what catches the "... is not a function" class of drift before runtime does.
Comment on lines +38 to +40

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this sanity check section is a particularly useful addition at this point. It's just checking that the code generator generated the code it was told to do, and we only reach it if the code generator ran successfully.

missing=0
while read -r op; do
method="$(echo "$op" | sed -E 's/_([a-z])/\U\1/g')"
if ! grep -q "$method" client/app/purple_client/apis/PurpleApi.ts; then
echo "WARNING: operation '$op' -> '$method' is missing from the generated client." >&2
missing=1
fi
done < <(grep -oE 'operationId: [A-Za-z0-9_]+' purple_api.yaml | awk '{print $2}' | sort -u)

if [ "$missing" -ne 0 ]; then
echo "WARNING: purple client is OUT OF SYNC with purple_api.yaml (see above)." >&2
exit 1
fi

echo "purple client is in sync with the schema."