144 lines
6.0 KiB
Bash
Executable File
144 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Regenerates the gRPC C# files from the Communication project's .proto files.
|
|
#
|
|
# Background: protoc (linux/arm64) segfaults inside our Docker build container
|
|
# (Grpc.Tools). As a workaround the generated C# is checked into
|
|
# src/ZB.MOM.WW.ScadaBridge.Communication/ — SiteStreamGrpc/ for sitestream.proto,
|
|
# CentralControlGrpc/ for central_control.proto, and SiteCommandGrpc/ for
|
|
# site_command.proto — and the Protobuf ItemGroup in the .csproj is commented out,
|
|
# so Docker just compiles the checked-in files.
|
|
#
|
|
# Run this script ON YOUR DEV MACHINE whenever a .proto changes:
|
|
#
|
|
# docker/regen-proto.sh [sitestream|centralcontrol|sitecommand|all] (default: all)
|
|
#
|
|
# 1. Injects a Protobuf ItemGroup for the selected proto(s) so Grpc.Tools runs.
|
|
# 2. Deletes the stale checked-in C# so a failed regen is obvious.
|
|
# 3. dotnet build (regen writes fresh files to obj/).
|
|
# 4. Copies the regenerated files back into the source tree.
|
|
# 5. Restores the original csproj so no active Protobuf item is left behind.
|
|
#
|
|
# Only the SELECTED protos get a Protobuf item. Enabling one whose generated C#
|
|
# is still checked in would define every generated type twice, which is why the
|
|
# per-proto selection exists. central_control.proto imports sitestream.proto,
|
|
# but protoc resolves that from the project-relative path — the import needs no
|
|
# Protobuf item of its own.
|
|
#
|
|
# Once we move to a Dockerfile base image that ships a working linux/arm64
|
|
# protoc, this script can be retired and Docker can regen the protos on every
|
|
# build like every other normal .NET project.
|
|
|
|
set -euo pipefail
|
|
|
|
TARGET="${1:-all}"
|
|
case "$TARGET" in
|
|
sitestream|centralcontrol|sitecommand|all) ;;
|
|
*) echo "usage: $0 [sitestream|centralcontrol|sitecommand|all]" >&2; exit 2 ;;
|
|
esac
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
COMM_DIR="$REPO_ROOT/src/ZB.MOM.WW.ScadaBridge.Communication"
|
|
CSPROJ="$COMM_DIR/ZB.MOM.WW.ScadaBridge.Communication.csproj"
|
|
GEN="$COMM_DIR/obj/Debug/net10.0/Protos"
|
|
|
|
echo "=== Regenerating gRPC files ($TARGET) ==="
|
|
|
|
if [[ ! -f "$CSPROJ" ]]; then
|
|
echo "ERROR: csproj not found at $CSPROJ" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Backup so we can always restore the comment state, however this script ends.
|
|
# Leaving the csproj with an active Protobuf item is the one outcome that breaks
|
|
# Docker, so every exit path restores this copy — success, a failed `dotnet
|
|
# build` (ERR), or Ctrl-C (INT/TERM) mid-build. An EXIT trap covers all of
|
|
# those (bash runs it on any shell termination, signals included), and the
|
|
# guard on $BACKUP's existence makes it idempotent: the normal success path
|
|
# below restores explicitly for prompt feedback, so by the time the trap fires
|
|
# on a clean exit $BACKUP is already gone and it's a no-op instead of a second
|
|
# restore/rm.
|
|
BACKUP="$(mktemp)"
|
|
cp "$CSPROJ" "$BACKUP"
|
|
restore_csproj() {
|
|
if [[ -f "$BACKUP" ]]; then
|
|
cp "$BACKUP" "$CSPROJ"
|
|
rm -f "$BACKUP"
|
|
echo "Restored csproj from backup."
|
|
fi
|
|
}
|
|
trap restore_csproj EXIT
|
|
|
|
# 1. Inject an ItemGroup holding just the selected protos, immediately before
|
|
# the closing </Project>. The documented commented-out block is left alone.
|
|
python3 - "$CSPROJ" "$TARGET" <<'PY'
|
|
import pathlib, sys
|
|
|
|
csproj, target = pathlib.Path(sys.argv[1]), sys.argv[2]
|
|
|
|
protos = []
|
|
if target in ("sitestream", "all"):
|
|
protos.append("sitestream.proto")
|
|
if target in ("centralcontrol", "all"):
|
|
protos.append("central_control.proto")
|
|
if target in ("sitecommand", "all"):
|
|
protos.append("site_command.proto")
|
|
|
|
items = "\n".join(
|
|
f' <Protobuf Include="Protos\\{p}" GrpcServices="Both" />' for p in protos)
|
|
block = f" <ItemGroup>\n{items}\n </ItemGroup>\n\n</Project>"
|
|
|
|
src = csproj.read_text()
|
|
if "</Project>" not in src:
|
|
raise SystemExit("Couldn't find </Project> to inject the Protobuf ItemGroup before.")
|
|
csproj.write_text(src.replace("</Project>", block, 1))
|
|
PY
|
|
|
|
# 2. Delete the stale files so any failure to regen is obvious.
|
|
if [[ "$TARGET" == "sitestream" || "$TARGET" == "all" ]]; then
|
|
rm -f "$COMM_DIR/SiteStreamGrpc/Sitestream.cs" "$COMM_DIR/SiteStreamGrpc/SitestreamGrpc.cs"
|
|
fi
|
|
if [[ "$TARGET" == "centralcontrol" || "$TARGET" == "all" ]]; then
|
|
rm -f "$COMM_DIR/CentralControlGrpc/CentralControl.cs" \
|
|
"$COMM_DIR/CentralControlGrpc/CentralControlGrpc.cs"
|
|
fi
|
|
if [[ "$TARGET" == "sitecommand" || "$TARGET" == "all" ]]; then
|
|
rm -f "$COMM_DIR/SiteCommandGrpc/SiteCommand.cs" \
|
|
"$COMM_DIR/SiteCommandGrpc/SiteCommandGrpc.cs"
|
|
fi
|
|
|
|
# 3. Regenerate by building.
|
|
echo "Building Communication project (regen)..."
|
|
dotnet build "$CSPROJ" --nologo -v minimal | tail -5
|
|
|
|
# 4. Copy generated files back into the source tree.
|
|
if [[ "$TARGET" == "sitestream" || "$TARGET" == "all" ]]; then
|
|
mkdir -p "$COMM_DIR/SiteStreamGrpc"
|
|
cp "$GEN/Sitestream.cs" "$GEN/SitestreamGrpc.cs" "$COMM_DIR/SiteStreamGrpc/"
|
|
echo "Copied regenerated files to SiteStreamGrpc/"
|
|
fi
|
|
if [[ "$TARGET" == "centralcontrol" || "$TARGET" == "all" ]]; then
|
|
mkdir -p "$COMM_DIR/CentralControlGrpc"
|
|
cp "$GEN/CentralControl.cs" "$GEN/CentralControlGrpc.cs" "$COMM_DIR/CentralControlGrpc/"
|
|
echo "Copied regenerated files to CentralControlGrpc/"
|
|
fi
|
|
if [[ "$TARGET" == "sitecommand" || "$TARGET" == "all" ]]; then
|
|
mkdir -p "$COMM_DIR/SiteCommandGrpc"
|
|
cp "$GEN/SiteCommand.cs" "$GEN/SiteCommandGrpc.cs" "$COMM_DIR/SiteCommandGrpc/"
|
|
echo "Copied regenerated files to SiteCommandGrpc/"
|
|
fi
|
|
|
|
# 5. Restore the backed-up csproj — i.e. drop the injected ItemGroup — so Docker
|
|
# builds keep working. (Also covered by the EXIT trap above; calling it here
|
|
# too just gives the success path its "Restored..." feedback before "Done".)
|
|
restore_csproj
|
|
|
|
echo ""
|
|
echo "Done. Review and commit:"
|
|
echo " git diff src/ZB.MOM.WW.ScadaBridge.Communication/Protos/"
|
|
echo " git diff src/ZB.MOM.WW.ScadaBridge.Communication/SiteStreamGrpc/"
|
|
echo " git diff src/ZB.MOM.WW.ScadaBridge.Communication/CentralControlGrpc/"
|
|
echo " git diff src/ZB.MOM.WW.ScadaBridge.Communication/SiteCommandGrpc/"
|
|
echo " git diff -- src/ZB.MOM.WW.ScadaBridge.Communication/*.csproj # must be EMPTY"
|