From a9cb490ee4da8d9adef5ed177eb95211fe5b73e2 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Mon, 13 Jan 2025 14:27:27 +0800 Subject: [PATCH 01/11] fixes --- .../block/ftl/hotreload/v1/hotreload.pb.go | 518 ++++++++++++++++++ .../block/ftl/hotreload/v1/hotreload.proto | 61 +++ .../hotreloadpbconnect/hotreload.connect.go | 168 ++++++ common/schema/schema.go | 3 + .../languageplugin/plugin_client.go | 2 +- .../languageplugin/plugin_integration_test.go | 2 +- .../ftl/deployment/HotReloadHandler.java | 135 +++++ .../block/ftl/deployment/ModuleProcessor.java | 87 +-- .../ftl-runtime/common/runtime/pom.xml | 1 + .../xyz/block/ftl/runtime/HotReloadSetup.java | 66 --- .../io.quarkus.dev.spi.HotReplacementSetup | 1 - jvm-runtime/plugin/common/jvmcommon.go | 7 + 12 files changed, 901 insertions(+), 150 deletions(-) create mode 100644 backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go create mode 100644 backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto create mode 100644 backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go create mode 100644 jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java delete mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java delete mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go new file mode 100644 index 0000000000..6c8065e0b1 --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -0,0 +1,518 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.1 +// protoc (unknown) +// source: xyz/block/ftl/hotreload/v1/hotreload.proto + +package hotreloadpb + +import ( + v11 "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" + v12 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" + v1 "github.com/block/ftl/common/protos/xyz/block/ftl/schema/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type WatchRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchRequest) Reset() { + *x = WatchRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchRequest) ProtoMessage() {} + +func (x *WatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead. +func (*WatchRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{0} +} + +type WatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *WatchResponse_ReloadSuccess + // *WatchResponse_ReloadFailed + Event isWatchResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchResponse) Reset() { + *x = WatchResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchResponse) ProtoMessage() {} + +func (x *WatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead. +func (*WatchResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{1} +} + +func (x *WatchResponse) GetEvent() isWatchResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *WatchResponse) GetReloadSuccess() *ReloadSuccess { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadSuccess); ok { + return x.ReloadSuccess + } + } + return nil +} + +func (x *WatchResponse) GetReloadFailed() *ReloadFailed { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadFailed); ok { + return x.ReloadFailed + } + } + return nil +} + +type isWatchResponse_Event interface { + isWatchResponse_Event() +} + +type WatchResponse_ReloadSuccess struct { + ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` +} + +type WatchResponse_ReloadFailed struct { + ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` +} + +func (*WatchResponse_ReloadSuccess) isWatchResponse_Event() {} + +func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} + +type ReloadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadRequest) Reset() { + *x = ReloadRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadRequest) ProtoMessage() {} + +func (x *ReloadRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadRequest.ProtoReflect.Descriptor instead. +func (*ReloadRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} +} + +type ReloadResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *ReloadResponse_ReloadSuccess + // *ReloadResponse_ReloadFailed + Event isReloadResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadResponse) Reset() { + *x = ReloadResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadResponse) ProtoMessage() {} + +func (x *ReloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadResponse.ProtoReflect.Descriptor instead. +func (*ReloadResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} +} + +func (x *ReloadResponse) GetEvent() isReloadResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *ReloadResponse) GetReloadSuccess() *ReloadSuccess { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadSuccess); ok { + return x.ReloadSuccess + } + } + return nil +} + +func (x *ReloadResponse) GetReloadFailed() *ReloadFailed { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadFailed); ok { + return x.ReloadFailed + } + } + return nil +} + +type isReloadResponse_Event interface { + isReloadResponse_Event() +} + +type ReloadResponse_ReloadSuccess struct { + ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` +} + +type ReloadResponse_ReloadFailed struct { + ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` +} + +func (*ReloadResponse_ReloadSuccess) isReloadResponse_Event() {} + +func (*ReloadResponse_ReloadFailed) isReloadResponse_Event() {} + +type ReloadSuccess struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Module schema for the built module + Module *v1.Module `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + // Module schema for the built module + Errors *v11.ErrorList `protobuf:"bytes,2,opt,name=errors,proto3" json:"errors,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadSuccess) Reset() { + *x = ReloadSuccess{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadSuccess) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadSuccess) ProtoMessage() {} + +func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. +func (*ReloadSuccess) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} +} + +func (x *ReloadSuccess) GetModule() *v1.Module { + if x != nil { + return x.Module + } + return nil +} + +func (x *ReloadSuccess) GetErrors() *v11.ErrorList { + if x != nil { + return x.Errors + } + return nil +} + +type ReloadFailed struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Module schema for the built module + Errors *v11.ErrorList `protobuf:"bytes,1,opt,name=errors,proto3" json:"errors,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadFailed) Reset() { + *x = ReloadFailed{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadFailed) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadFailed) ProtoMessage() {} + +func (x *ReloadFailed) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. +func (*ReloadFailed) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} +} + +func (x *ReloadFailed) GetErrors() *v11.ErrorList { + if x != nil { + return x.Errors + } + return nil +} + +var File_xyz_block_ftl_hotreload_v1_hotreload_proto protoreflect.FileDescriptor + +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, + 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x74, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x1a, 0x28, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x24, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, + 0x6c, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x74, 0x6c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0e, 0x0a, 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0xbd, 0x01, 0x0a, 0x0d, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, + 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, + 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, + 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, + 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, + 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, + 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, + 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, + 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5f, + 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x4e, 0x50, 0x01, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescOnce sync.Once + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData = file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc +) + +func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescOnce.Do(func() { + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData = protoimpl.X.CompressGZIP(file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData) + }) + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData +} + +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ + (*WatchRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.WatchRequest + (*WatchResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.WatchResponse + (*ReloadRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadRequest + (*ReloadResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadResponse + (*ReloadSuccess)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 5: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 6: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 7: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 8: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 9: xyz.block.ftl.v1.PingResponse +} +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ + 4, // 0: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 5, // 1: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 4, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 5, // 3: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 6, // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 7, // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 7, // 6: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 8, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest + 2, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 9, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse + 3, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 10, // [10:13] is the sub-list for method output_type + 7, // [7:10] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } +func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { + if File_xyz_block_ftl_hotreload_v1_hotreload_proto != nil { + return + } + file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1].OneofWrappers = []any{ + (*WatchResponse_ReloadSuccess)(nil), + (*WatchResponse_ReloadFailed)(nil), + } + file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3].OneofWrappers = []any{ + (*ReloadResponse_ReloadSuccess)(nil), + (*ReloadResponse_ReloadFailed)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes, + DependencyIndexes: file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs, + MessageInfos: file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes, + }.Build() + File_xyz_block_ftl_hotreload_v1_hotreload_proto = out.File + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = nil + file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = nil + file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = nil +} diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto new file mode 100644 index 0000000000..c7f26cc81b --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package xyz.block.ftl.hotreload.v1; + +import "xyz/block/ftl/language/v1/language.proto"; +import "xyz/block/ftl/schema/v1/schema.proto"; +import "xyz/block/ftl/v1/ftl.proto"; + +option go_package = "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb"; +option java_multiple_files = true; + +message WatchRequest { + +} + +message WatchResponse { + oneof event { + ReloadSuccess reload_success = 1; + ReloadFailed reload_failed = 2; + } +} + +message ReloadRequest { +} + +message ReloadResponse { + + oneof event { + ReloadSuccess reload_success = 1; + ReloadFailed reload_failed = 2; + } +} + +message ReloadSuccess { + // Module schema for the built module + ftl.schema.v1.Module module = 1; + // Module schema for the built module + ftl.language.v1.ErrorList errors = 2; +} + +message ReloadFailed { + // Module schema for the built module + ftl.language.v1.ErrorList errors = 1; +} + +// HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload +service HotReloadService { + // Ping service for readiness. + rpc Ping(xyz.block.ftl.v1.PingRequest) returns (xyz.block.ftl.v1.PingResponse) { + option idempotency_level = NO_SIDE_EFFECTS; + } + + // Watch returns a stream that is notified of any Reloads that are not explicit + rpc Watch(WatchRequest) returns (stream WatchResponse); + + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + // + rpc Reload(ReloadRequest) returns (ReloadResponse); + +} diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go new file mode 100644 index 0000000000..9bfeae81cc --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go @@ -0,0 +1,168 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: xyz/block/ftl/hotreload/v1/hotreload.proto + +package hotreloadpbconnect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + v11 "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + v1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_7_0 + +const ( + // HotReloadServiceName is the fully-qualified name of the HotReloadService service. + HotReloadServiceName = "xyz.block.ftl.hotreload.v1.HotReloadService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // HotReloadServicePingProcedure is the fully-qualified name of the HotReloadService's Ping RPC. + HotReloadServicePingProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Ping" + // HotReloadServiceWatchProcedure is the fully-qualified name of the HotReloadService's Watch RPC. + HotReloadServiceWatchProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Watch" + // HotReloadServiceReloadProcedure is the fully-qualified name of the HotReloadService's Reload RPC. + HotReloadServiceReloadProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Reload" +) + +// HotReloadServiceClient is a client for the xyz.block.ftl.hotreload.v1.HotReloadService service. +type HotReloadServiceClient interface { + // Ping service for readiness. + Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) + // Watch returns a stream that is notified of any Reloads that are not explicit + Watch(context.Context, *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) +} + +// NewHotReloadServiceClient constructs a client for the xyz.block.ftl.hotreload.v1.HotReloadService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) HotReloadServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + return &hotReloadServiceClient{ + ping: connect.NewClient[v1.PingRequest, v1.PingResponse]( + httpClient, + baseURL+HotReloadServicePingProcedure, + connect.WithIdempotency(connect.IdempotencyNoSideEffects), + connect.WithClientOptions(opts...), + ), + watch: connect.NewClient[v11.WatchRequest, v11.WatchResponse]( + httpClient, + baseURL+HotReloadServiceWatchProcedure, + opts..., + ), + reload: connect.NewClient[v11.ReloadRequest, v11.ReloadResponse]( + httpClient, + baseURL+HotReloadServiceReloadProcedure, + opts..., + ), + } +} + +// hotReloadServiceClient implements HotReloadServiceClient. +type hotReloadServiceClient struct { + ping *connect.Client[v1.PingRequest, v1.PingResponse] + watch *connect.Client[v11.WatchRequest, v11.WatchResponse] + reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] +} + +// Ping calls xyz.block.ftl.hotreload.v1.HotReloadService.Ping. +func (c *hotReloadServiceClient) Ping(ctx context.Context, req *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) { + return c.ping.CallUnary(ctx, req) +} + +// Watch calls xyz.block.ftl.hotreload.v1.HotReloadService.Watch. +func (c *hotReloadServiceClient) Watch(ctx context.Context, req *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) { + return c.watch.CallServerStream(ctx, req) +} + +// Reload calls xyz.block.ftl.hotreload.v1.HotReloadService.Reload. +func (c *hotReloadServiceClient) Reload(ctx context.Context, req *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { + return c.reload.CallUnary(ctx, req) +} + +// HotReloadServiceHandler is an implementation of the xyz.block.ftl.hotreload.v1.HotReloadService +// service. +type HotReloadServiceHandler interface { + // Ping service for readiness. + Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) + // Watch returns a stream that is notified of any Reloads that are not explicit + Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) +} + +// NewHotReloadServiceHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + hotReloadServicePingHandler := connect.NewUnaryHandler( + HotReloadServicePingProcedure, + svc.Ping, + connect.WithIdempotency(connect.IdempotencyNoSideEffects), + connect.WithHandlerOptions(opts...), + ) + hotReloadServiceWatchHandler := connect.NewServerStreamHandler( + HotReloadServiceWatchProcedure, + svc.Watch, + opts..., + ) + hotReloadServiceReloadHandler := connect.NewUnaryHandler( + HotReloadServiceReloadProcedure, + svc.Reload, + opts..., + ) + return "/xyz.block.ftl.hotreload.v1.HotReloadService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case HotReloadServicePingProcedure: + hotReloadServicePingHandler.ServeHTTP(w, r) + case HotReloadServiceWatchProcedure: + hotReloadServiceWatchHandler.ServeHTTP(w, r) + case HotReloadServiceReloadProcedure: + hotReloadServiceReloadHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedHotReloadServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedHotReloadServiceHandler struct{} + +func (UnimplementedHotReloadServiceHandler) Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Ping is not implemented")) +} + +func (UnimplementedHotReloadServiceHandler) Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Watch is not implemented")) +} + +func (UnimplementedHotReloadServiceHandler) Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Reload is not implemented")) +} diff --git a/common/schema/schema.go b/common/schema/schema.go index 14f2fe3e76..706e4ad360 100644 --- a/common/schema/schema.go +++ b/common/schema/schema.go @@ -210,6 +210,9 @@ func TypeName(v any) string { // FromProto converts a protobuf Schema to a Schema and validates it. func FromProto(s *schemapb.Schema) (*Schema, error) { + if s == nil { + return &Schema{}, nil + } modules, err := moduleListToSchema(s.Modules) if err != nil { return nil, err diff --git a/internal/buildengine/languageplugin/plugin_client.go b/internal/buildengine/languageplugin/plugin_client.go index 6550a95ec4..73f6d59299 100644 --- a/internal/buildengine/languageplugin/plugin_client.go +++ b/internal/buildengine/languageplugin/plugin_client.go @@ -62,7 +62,7 @@ func (p *pluginClientImpl) start(ctx context.Context, dir, language, name string } envvars := []string{"FTL_NAME=" + name} plugin, cmdCtx, err := plugin.Spawn(ctx, - log.FromContext(ctx).GetLevel(), + log.Info, name, name, dir, diff --git a/internal/buildengine/languageplugin/plugin_integration_test.go b/internal/buildengine/languageplugin/plugin_integration_test.go index 413241bd5e..93c79a88e6 100644 --- a/internal/buildengine/languageplugin/plugin_integration_test.go +++ b/internal/buildengine/languageplugin/plugin_integration_test.go @@ -66,7 +66,7 @@ const ( func TestBuilds(t *testing.T) { sch := generateInitialSchema(t) in.Run(t, - in.WithLanguages("go"), + in.WithLanguages("java"), in.WithoutController(), in.WithoutProvisioner(), in.WithoutTimeline(), diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java new file mode 100644 index 0000000000..5e99b057db --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -0,0 +1,135 @@ +package xyz.block.ftl.deployment; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; +import org.jboss.logging.Logger; +import xyz.block.ftl.hotreload.v1.HotReloadServiceGrpc; +import xyz.block.ftl.hotreload.v1.ReloadFailed; +import xyz.block.ftl.hotreload.v1.ReloadRequest; +import xyz.block.ftl.hotreload.v1.ReloadResponse; +import xyz.block.ftl.hotreload.v1.ReloadSuccess; +import xyz.block.ftl.language.v1.Error; +import xyz.block.ftl.language.v1.ErrorList; +import xyz.block.ftl.schema.v1.Module; +import xyz.block.ftl.v1.PingRequest; +import xyz.block.ftl.v1.PingResponse; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplBase { + + static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + static volatile Module module; + + private static HotReloadHandler INSTANCE; + private static Server server; + + @Override + public void ping(PingRequest request, StreamObserver responseObserver) { + responseObserver.onNext(PingResponse.newBuilder().build()); + } + + @Override + public void reload(ReloadRequest request, StreamObserver responseObserver) { + doScan(true); + Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); + Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); + if (compileProblem != null || deploymentProblems != null) { + ErrorList.Builder builder = ErrorList.newBuilder(); + if (compileProblem != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(xyz.block.ftl.language.v1.Error.ErrorType.ERROR_TYPE_COMPILER) + .setMsg(compileProblem.getMessage()) + .build()); + } + if (deploymentProblems != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(Error.ErrorType.ERROR_TYPE_FTL) + .setMsg(deploymentProblems.getMessage()) + .build()); + } + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(builder).build()).build()); + responseObserver.onCompleted(); + } else if (module != null) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder() + .setModule(module).build()).build()); + } else { + responseObserver.onError(new RuntimeException("schema not generated")); + } + } + + public static void start() { + if (INSTANCE == null) { + return; + } + INSTANCE = new HotReloadHandler(); + + for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { + Path migrations = dir.resolve("db"); + if (Files.isDirectory(migrations)) { + try (var stream = Files.walk(migrations)) { + stream.forEach(existingMigrations::add); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + int port = Integer.getInteger("ftl.language.port"); + try { + server = ServerBuilder.forPort(port) + .addService(INSTANCE) + .build() + .start(); + ((QuarkusClassLoader)HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { + @Override + public void run() { + server.shutdownNow(); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + static void doScan(boolean force) { + if (RuntimeUpdatesProcessor.INSTANCE != null) { + try { + AtomicBoolean newForce = new AtomicBoolean(); + for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { + Path migrations = dir.resolve("db"); + if (Files.isDirectory(migrations)) { + try (var stream = Files.walk(migrations)) { + stream.forEach(p -> { + if (p.getFileName().toString().endsWith(".sql")) { + if (existingMigrations.add(p)) { + newForce.set(true); + } + } + }); + } + } + } + RuntimeUpdatesProcessor.INSTANCE.doScan(force || newForce.get()); + } catch (Exception e) { + Logger.getLogger(HotReloadHandler.class).error("Failed to scan for changes", e); + } + } + } +} diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index b64a4f3354..67bb4de08e 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -2,7 +2,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -15,8 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Timer; -import java.util.TimerTask; import java.util.stream.Collectors; import org.jboss.jandex.DotName; @@ -25,7 +22,6 @@ import org.tomlj.TomlParseResult; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -41,15 +37,12 @@ import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; -import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.grpc.deployment.BindableServiceBuildItem; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.util.HashUtil; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; -import xyz.block.ftl.language.v1.Error; -import xyz.block.ftl.language.v1.ErrorList; import xyz.block.ftl.runtime.FTLDatasourceCredentials; import xyz.block.ftl.runtime.FTLRecorder; import xyz.block.ftl.runtime.JsonSerializationConfig; @@ -91,76 +84,6 @@ public SystemPropertyBuildItem moduleNameConfig(ApplicationInfoBuildItem applica return new SystemPropertyBuildItem("ftl.module.name", applicationInfoBuildItem.getName()); } - private static volatile Timer devModeProblemTimer; - - @BuildStep(onlyIf = IsDevelopment.class) - @Record(ExecutionTime.STATIC_INIT) - public void reportDevModeProblems(FTLRecorder recorder, OutputTargetBuildItem outputTargetBuildItem) { - if (devModeProblemTimer != null) { - return; - } - Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); - devModeProblemTimer = new Timer("FTL Dev Mode Error Report", true); - devModeProblemTimer.schedule(new TimerTask() { - - String errorHash; - - @Override - public void run() { - Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); - Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); - if (compileProblem != null || deploymentProblems != null) { - ErrorList.Builder builder = ErrorList.newBuilder(); - if (compileProblem != null) { - builder.addErrors(Error.newBuilder() - .setLevel(Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(Error.ErrorType.ERROR_TYPE_COMPILER) - .setMsg(compileProblem.getMessage()) - .build()); - } - if (deploymentProblems != null) { - builder.addErrors(Error.newBuilder() - .setLevel(Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(Error.ErrorType.ERROR_TYPE_FTL) - .setMsg(deploymentProblems.getMessage()) - .build()); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (var out = Files.newOutputStream(errorOutput)) { - builder.build().writeTo(baos); - errorHash = HashUtil.sha256(baos.toByteArray()); - builder.build().writeTo(out); - } catch (IOException e) { - log.error("Failed to write error list", e); - } - } else if (errorHash != null) { - if (!Files.exists(errorOutput)) { - // File already cleared - errorHash = null; - } else { - try { - var currentHash = HashUtil.sha256(Files.readAllBytes(errorOutput)); - if (currentHash.equals(errorHash)) { - try (OutputStream output = Files.newOutputStream(errorOutput)) { - ErrorList.newBuilder().build().writeTo(output); - } - } - } catch (IOException e) { - log.errorf("Failed to read error list", e); - } - } - } - } - }, 1000, 1000); - ((QuarkusClassLoader) ModuleProcessor.class.getClassLoader()).addCloseTask(new Runnable() { - @Override - public void run() { - devModeProblemTimer.cancel(); - devModeProblemTimer = null; - } - }); - } - @BuildStep ModuleNameBuildItem moduleName(ApplicationInfoBuildItem applicationInfoBuildItem, ApplicationArchivesBuildItem archivesBuildItem) throws IOException { @@ -250,6 +173,10 @@ public void generateSchema(CombinedIndexBuildItem index, var schBytes = sch.toByteArray(); var errBytes = err.toByteArray(); + recorder.loadModuleContextOnStartup(); + + Files.write(output, schBytes); + Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to @@ -269,11 +196,9 @@ public void generateSchema(CombinedIndexBuildItem index, .produce(new SystemPropertyBuildItem(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH, path.toString())); recorder.handleDevModeRunnerStart(shutdownContextBuildItem); } + // TODO: replace runner info file as well + HotReloadHandler.start(); } - recorder.loadModuleContextOnStartup(); - - Files.write(output, schBytes); - Files.write(errorOutput, errBytes); output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); try (var out = Files.newOutputStream(output)) { diff --git a/jvm-runtime/ftl-runtime/common/runtime/pom.xml b/jvm-runtime/ftl-runtime/common/runtime/pom.xml index 89362d3694..14f4eb9f14 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/pom.xml +++ b/jvm-runtime/ftl-runtime/common/runtime/pom.xml @@ -164,6 +164,7 @@ **/lease.proto **/ftl.proto **/language.proto + **/hotreload.proto **/publish.proto **/pubsub.proto diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java deleted file mode 100644 index 52900ddaf2..0000000000 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java +++ /dev/null @@ -1,66 +0,0 @@ -package xyz.block.ftl.runtime; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.jboss.logging.Logger; - -import io.quarkus.dev.spi.HotReplacementContext; -import io.quarkus.dev.spi.HotReplacementSetup; - -public class HotReloadSetup implements HotReplacementSetup { - - static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - static volatile HotReplacementContext context; - private static volatile String errorOutputPath; - private static final String ERRORS_OUT = "errors.pb"; - - @Override - public void setupHotDeployment(HotReplacementContext hrc) { - context = hrc; - for (var dir : context.getSourcesDir()) { - Path migrations = dir.resolve("db"); - if (Files.isDirectory(migrations)) { - try (var stream = Files.walk(migrations)) { - stream.forEach(p -> { - existingMigrations.add(p); - - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - } - - static void doScan(boolean force) { - if (context != null) { - try { - AtomicBoolean newForce = new AtomicBoolean(); - for (var dir : context.getSourcesDir()) { - Path migrations = dir.resolve("db"); - if (Files.isDirectory(migrations)) { - try (var stream = Files.walk(migrations)) { - stream.forEach(p -> { - if (p.getFileName().toString().endsWith(".sql")) { - if (existingMigrations.add(p)) { - newForce.set(true); - } - } - }); - } - } - } - context.doScan(force || newForce.get()); - } catch (Exception e) { - Logger.getLogger(HotReloadSetup.class).error("Failed to scan for changes", e); - } - } - } -} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup b/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup deleted file mode 100644 index f02b221da3..0000000000 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup +++ /dev/null @@ -1 +0,0 @@ -xyz.block.ftl.runtime.HotReloadSetup \ No newline at end of file diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index fa64f54ae0..41a4eceba9 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -365,6 +365,12 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if err == nil { devModeBuild = fmt.Sprintf("%s -Ddebug=%d", devModeBuild, debugPort.Port) } + protoPort, err := plugin.AllocatePort() + if err != nil { + return fmt.Errorf("could not allocate port: %w", err) + } + devModeBuild = fmt.Sprintf("%s -Dftl.language.port=%d", devModeBuild, protoPort.Port) + if os.Getenv("FTL_SUSPEND") == "true" { devModeBuild += " -Dsuspend " } @@ -620,6 +626,7 @@ func (s *Service) BuildContextUpdated(ctx context.Context, req *connect.Request[ if err != nil { return nil, err } + s.writeGenericSchemaFiles(ctx, buildCtx.Schema, buildCtx.Config) err = s.writeGenericSchemaFiles(ctx, buildCtx.Schema, buildCtx.Config) if err != nil { From b2bab6e9212e9957459962b8632401fd1f2af7f7 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Mon, 13 Jan 2025 14:54:45 +0800 Subject: [PATCH 02/11] tmp --- .../block/ftl/hotreload/v1/hotreload.pb.go | 109 ++++++++++-------- .../block/ftl/hotreload/v1/hotreload.proto | 1 + .../ftl/deployment/HotReloadHandler.java | 29 ++--- .../block/ftl/deployment/ModuleProcessor.java | 7 -- .../xyz/block/ftl/runtime/FTLRecorder.java | 18 --- jvm-runtime/plugin/common/jvmcommon.go | 61 +++++++--- 6 files changed, 124 insertions(+), 101 deletions(-) diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go index 6c8065e0b1..c145b82b95 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -143,6 +143,7 @@ func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} type ReloadRequest struct { state protoimpl.MessageState `protogen:"open.v1"` + Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -177,6 +178,13 @@ func (*ReloadRequest) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} } +func (x *ReloadRequest) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + type ReloadResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Event: @@ -385,57 +393,58 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, - 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, - 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, - 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, - 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, - 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, - 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, - 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, - 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, - 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, - 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x76, 0x65, 0x6e, 0x74, 0x22, 0x25, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0e, + 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, + 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x86, 0x01, 0x0a, + 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, + 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, + 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, + 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, + 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5f, - 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, - 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x4e, 0x50, 0x01, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, - 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x30, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, + 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, + 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, + 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index c7f26cc81b..17b8bdfce4 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -21,6 +21,7 @@ message WatchResponse { } message ReloadRequest { + bool force = 1; } message ReloadResponse { diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index 5e99b057db..fcb9b6e48a 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -1,11 +1,20 @@ package xyz.block.ftl.deployment; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.jboss.logging.Logger; + import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; -import org.jboss.logging.Logger; import xyz.block.ftl.hotreload.v1.HotReloadServiceGrpc; import xyz.block.ftl.hotreload.v1.ReloadFailed; import xyz.block.ftl.hotreload.v1.ReloadRequest; @@ -17,14 +26,6 @@ import xyz.block.ftl.v1.PingRequest; import xyz.block.ftl.v1.PingResponse; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplBase { static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -41,7 +42,7 @@ public void ping(PingRequest request, StreamObserver responseObser @Override public void reload(ReloadRequest request, StreamObserver responseObserver) { - doScan(true); + doScan(request.getForce()); Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); if (compileProblem != null || deploymentProblems != null) { @@ -62,12 +63,14 @@ public void reload(ReloadRequest request, StreamObserver respons } responseObserver.onNext(ReloadResponse.newBuilder() .setReloadFailed(ReloadFailed.newBuilder() - .setErrors(builder).build()).build()); + .setErrors(builder).build()) + .build()); responseObserver.onCompleted(); } else if (module != null) { responseObserver.onNext(ReloadResponse.newBuilder() .setReloadSuccess(ReloadSuccess.newBuilder() - .setModule(module).build()).build()); + .setModule(module).build()) + .build()); } else { responseObserver.onError(new RuntimeException("schema not generated")); } @@ -96,7 +99,7 @@ public static void start() { .addService(INSTANCE) .build() .start(); - ((QuarkusClassLoader)HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { + ((QuarkusClassLoader) HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { @Override public void run() { server.shutdownNow(); diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 67bb4de08e..67d20fc4f6 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -22,7 +22,6 @@ import org.tomlj.TomlParseResult; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -244,10 +243,4 @@ void openSocket(BuildProducer virtual, socket.produce(RequireSocketHttpBuildItem.MARKER); virtual.produce(RequireVirtualHttpBuildItem.MARKER); } - - @Record(ExecutionTime.RUNTIME_INIT) - @BuildStep(onlyIf = IsDevelopment.class) - void hotReload(ShutdownContextBuildItem shutdownContextBuildItem, FTLRecorder recorder) { - recorder.startReloadTimer(shutdownContextBuildItem); - } } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java index 8edd90fb34..a4db234ff3 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java @@ -3,8 +3,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; -import java.util.Timer; -import java.util.TimerTask; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.parameters.ParameterExtractor; @@ -157,22 +155,6 @@ public Object extractParameter(ResteasyReactiveRequestContext context) { } } - public void startReloadTimer(ShutdownContext shutdownContext) { - Timer t = new Timer("FTL Hot Reload Timer", true); - t.schedule(new TimerTask() { - @Override - public void run() { - HotReloadSetup.doScan(false); - } - }, 1000, 1000); - shutdownContext.addShutdownTask(new Runnable() { - @Override - public void run() { - t.cancel(); - } - }); - } - public void registerDatabase(String dbKind, GetDeploymentContextResponse.DbType name) { FTLController.instance().registerDatabase(dbKind, name); } diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 41a4eceba9..d130053d47 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -24,11 +24,14 @@ import ( "github.com/beevik/etree" "github.com/block/scaffolder" "github.com/go-viper/mapstructure/v2" + "github.com/jpillora/backoff" "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "github.com/block/ftl" + hotreloadpb "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect" langpb "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" langconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1/languagepbconnect" ftlv1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" @@ -45,6 +48,7 @@ import ( "github.com/block/ftl/internal/flock" "github.com/block/ftl/internal/log" "github.com/block/ftl/internal/moduleconfig" + "github.com/block/ftl/internal/rpc" "github.com/block/ftl/internal/watch" ) @@ -357,7 +361,8 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } ctx = log.ContextWithLogger(ctx, logger) - bind := fmt.Sprintf("http://localhost:%d", address.Port) + devModeEndpoint := fmt.Sprintf("http://localhost:%d", address.Port) + bind := devModeEndpoint devModeBuild := buildCtx.Config.DevModeBuild debugPort, err := plugin.AllocatePort() debugPort32 := int32(debugPort.Port) @@ -391,7 +396,14 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb }() forceUpdate := false - schemaChangeTicker := time.NewTicker(100 * time.Millisecond) + // Wait for the plugin to start. + client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, fmt.Sprintf("http://localhost:%d", protoPort), log.Trace) + err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) + if err != nil { + return fmt.Errorf("timed out waiting for start %w", err) + } + + schemaChangeTicker := time.NewTicker(500 * time.Millisecond) defer schemaChangeTicker.Stop() for { select { @@ -411,20 +423,15 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb case bc := <-events: buildCtx = bc.buildCtx forceUpdate = true - - // Force a hot reload by sending a HEAD request to the dev server - req, err := http.NewRequestWithContext(ctx, http.MethodHead, bind, nil) + result, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: true})) if err != nil { - logger.Errorf(err, "could not create request to force build context update") - continue + return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } - resp, err := http.DefaultClient.Do(req) + resp := s.toBuildResponse(result.Msg, &bc.buildCtx, false, devModeEndpoint, debugPort32, runnerInfoFile) + err = stream.Send(resp) if err != nil { - logger.Errorf(err, "could not send request to force build context update") - continue + return fmt.Errorf("failed to send response %w", err) } - resp.Body.Close() - case <-schemaChangeTicker.C: changed := false @@ -497,7 +504,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb ContextId: buildCtx.ID, IsAutomaticRebuild: auto, Module: moduleProto, - DevEndpoint: ptr(fmt.Sprintf("http://localhost:%d", address.Port)), + DevEndpoint: ptr(devModeEndpoint), DevRunnerInfoFile: &runnerInfoFile, DebugPort: &debugPort32, Deploy: []string{SchemaFile}, @@ -952,3 +959,31 @@ func (s *Service) writeGenericSchemaFiles(ctx context.Context, v *schema.Schema, } return nil } + +func (s *Service) toBuildResponse(result *hotreloadpb.ReloadResponse, bc *buildContext, auto bool, devEndpoint string, debugPort int32, devRunnerInfoFile string) (ret *langpb.BuildResponse) { + ret = &langpb.BuildResponse{} + switch e := result.Event.(type) { + case *hotreloadpb.ReloadResponse_ReloadSuccess: + ret.Event = &langpb.BuildResponse_BuildSuccess{ + BuildSuccess: &langpb.BuildSuccess{ + ContextId: bc.ID, + IsAutomaticRebuild: auto, + Module: e.ReloadSuccess.Module, + Errors: e.ReloadSuccess.Errors, + DevEndpoint: &devEndpoint, + DebugPort: &debugPort, + DevRunnerInfoFile: &devRunnerInfoFile, + }, + } + + case *hotreloadpb.ReloadResponse_ReloadFailed: + ret.Event = &langpb.BuildResponse_BuildFailure{ + BuildFailure: &langpb.BuildFailure{ + ContextId: bc.ID, + IsAutomaticRebuild: auto, + Errors: e.ReloadFailed.Errors, + }, + } + } + return nil +} From df001df06bd708e0a33a39c41c6edbcdbd99e500 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 17 Jan 2025 08:27:59 +1100 Subject: [PATCH 03/11] tmp --- .../block/ftl/hotreload/v1/hotreload.proto | 6 +- .../ftl/deployment/HotReloadHandler.java | 11 ++-- .../block/ftl/deployment/ModuleProcessor.java | 59 +++++++++---------- jvm-runtime/plugin/common/jvmcommon.go | 1 - 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index 17b8bdfce4..882bca0395 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -9,9 +9,7 @@ import "xyz/block/ftl/v1/ftl.proto"; option go_package = "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb"; option java_multiple_files = true; -message WatchRequest { - -} +message WatchRequest {} message WatchResponse { oneof event { @@ -25,7 +23,6 @@ message ReloadRequest { } message ReloadResponse { - oneof event { ReloadSuccess reload_success = 1; ReloadFailed reload_failed = 2; @@ -58,5 +55,4 @@ service HotReloadService { // such as when the Reload context changes. // rpc Reload(ReloadRequest) returns (ReloadResponse); - } diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index fcb9b6e48a..94fc864163 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -31,9 +31,8 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); static volatile Module module; - - private static HotReloadHandler INSTANCE; - private static Server server; + private static final AtomicBoolean started = new AtomicBoolean(); + private static volatile Server server; @Override public void ping(PingRequest request, StreamObserver responseObserver) { @@ -77,10 +76,10 @@ public void reload(ReloadRequest request, StreamObserver respons } public static void start() { - if (INSTANCE == null) { + + if(!started.compareAndSet(false, true)) { return; } - INSTANCE = new HotReloadHandler(); for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { Path migrations = dir.resolve("db"); @@ -96,7 +95,7 @@ public static void start() { int port = Integer.getInteger("ftl.language.port"); try { server = ServerBuilder.forPort(port) - .addService(INSTANCE) + .addService(new HotReloadHandler()) .build() .start(); ((QuarkusClassLoader) HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 67d20fc4f6..a4a6ac2a5c 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -181,40 +181,39 @@ public void generateSchema(CombinedIndexBuildItem index, // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. var hash = HashUtil.sha256(schBytes); - if (Objects.equals(hash, schemaHash)) { - return; - } - schemaHash = hash; - String runnerInfo = System.getenv(DEV_MODE_RUNNER_INFO_FILE); - if (runnerInfo != null) { - Path path = Path.of(runnerInfo); - // Delete the runner info file if it already exists - Files.deleteIfExists(path); - // This method tells the runtime not to actually start until we have updated runner details - systemPropertyBuildItemBuildProducer - .produce(new SystemPropertyBuildItem(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH, path.toString())); - recorder.handleDevModeRunnerStart(shutdownContextBuildItem); + if (!Objects.equals(hash, schemaHash)) { + schemaHash = hash; + String runnerInfo = System.getenv(DEV_MODE_RUNNER_INFO_FILE); + if (runnerInfo != null) { + Path path = Path.of(runnerInfo); + // Delete the runner info file if it already exists + Files.deleteIfExists(path); + // This method tells the runtime not to actually start until we have updated runner details + systemPropertyBuildItemBuildProducer + .produce(new SystemPropertyBuildItem(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH, path.toString())); + recorder.handleDevModeRunnerStart(shutdownContextBuildItem); + } } // TODO: replace runner info file as well HotReloadHandler.start(); + } else { + output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); + try (var out = Files.newOutputStream(output)) { + out.write( + """ + #!/bin/bash + if [ -n "$FTL_DEBUG_PORT" ]; then + FTL_JVM_OPTS="$FTL_JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$FTL_DEBUG_PORT" + fi + exec java $FTL_JVM_OPTS -jar quarkus-app/quarkus-run.jar""" + .getBytes(StandardCharsets.UTF_8)); + } + var perms = Files.getPosixFilePermissions(output); + EnumSet newPerms = EnumSet.copyOf(perms); + newPerms.add(PosixFilePermission.GROUP_EXECUTE); + newPerms.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(output, newPerms); } - - output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); - try (var out = Files.newOutputStream(output)) { - out.write( - """ - #!/bin/bash - if [ -n "$FTL_DEBUG_PORT" ]; then - FTL_JVM_OPTS="$FTL_JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$FTL_DEBUG_PORT" - fi - exec java $FTL_JVM_OPTS -jar quarkus-app/quarkus-run.jar""" - .getBytes(StandardCharsets.UTF_8)); - } - var perms = Files.getPosixFilePermissions(output); - EnumSet newPerms = EnumSet.copyOf(perms); - newPerms.add(PosixFilePermission.GROUP_EXECUTE); - newPerms.add(PosixFilePermission.OWNER_EXECUTE); - Files.setPosixFilePermissions(output, newPerms); } @BuildStep diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index d130053d47..84c939bb1c 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io/fs" - "net/http" "os" "path/filepath" "reflect" From 7ee65dfd4ccf37d6cd71543effe2506cd9cc9981 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 17 Jan 2025 08:55:35 +1100 Subject: [PATCH 04/11] fixes --- .../ftl/deployment/HotReloadHandler.java | 27 +++++++++++-------- jvm-runtime/plugin/common/jvmcommon.go | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index 94fc864163..d7524dac7c 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -28,6 +28,8 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplBase { + private static final Logger LOG = Logger.getLogger(HotReloadHandler.class); + static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); static volatile Module module; @@ -37,6 +39,7 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB @Override public void ping(PingRequest request, StreamObserver responseObserver) { responseObserver.onNext(PingResponse.newBuilder().build()); + responseObserver.onCompleted(); } @Override @@ -77,7 +80,7 @@ public void reload(ReloadRequest request, StreamObserver respons public static void start() { - if(!started.compareAndSet(false, true)) { + if (!started.compareAndSet(false, true)) { return; } @@ -93,21 +96,23 @@ public static void start() { } int port = Integer.getInteger("ftl.language.port"); + server = ServerBuilder.forPort(port) + .addService(new HotReloadHandler()) + .build(); try { - server = ServerBuilder.forPort(port) - .addService(new HotReloadHandler()) - .build() - .start(); - ((QuarkusClassLoader) HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { - @Override - public void run() { - server.shutdownNow(); - } - }); + LOG.info("Starting Hot Reload gRPC server on port " + port); + server.start(); } catch (IOException e) { throw new RuntimeException(e); } + ((QuarkusClassLoader) HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { + @Override + public void run() { + server.shutdownNow(); + } + }); + } static void doScan(boolean force) { diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 84c939bb1c..6e164d0c28 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -396,7 +396,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb forceUpdate := false // Wait for the plugin to start. - client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, fmt.Sprintf("http://localhost:%d", protoPort), log.Trace) + client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, fmt.Sprintf("http://localhost:%d", protoPort.Port), log.Trace) err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) if err != nil { return fmt.Errorf("timed out waiting for start %w", err) From 38ea562c2db098580460f4629b77ca507cb095c5 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 17 Jan 2025 09:13:23 +1100 Subject: [PATCH 05/11] tmp --- .../block/ftl/hotreload/v1/hotreload.pb.go | 225 +++--------------- .../block/ftl/hotreload/v1/hotreload.proto | 12 - .../hotreloadpbconnect/hotreload.connect.go | 28 --- .../languageplugin/plugin_client.go | 2 +- .../languageplugin/plugin_integration_test.go | 6 +- internal/integration/harness.go | 2 +- jvm-runtime/plugin/common/jvmcommon.go | 12 +- 7 files changed, 53 insertions(+), 234 deletions(-) diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go index c145b82b95..1b19c527a7 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -23,124 +23,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type WatchRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchRequest) Reset() { - *x = WatchRequest{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchRequest) ProtoMessage() {} - -func (x *WatchRequest) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead. -func (*WatchRequest) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{0} -} - -type WatchResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Event: - // - // *WatchResponse_ReloadSuccess - // *WatchResponse_ReloadFailed - Event isWatchResponse_Event `protobuf_oneof:"event"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchResponse) Reset() { - *x = WatchResponse{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchResponse) ProtoMessage() {} - -func (x *WatchResponse) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead. -func (*WatchResponse) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{1} -} - -func (x *WatchResponse) GetEvent() isWatchResponse_Event { - if x != nil { - return x.Event - } - return nil -} - -func (x *WatchResponse) GetReloadSuccess() *ReloadSuccess { - if x != nil { - if x, ok := x.Event.(*WatchResponse_ReloadSuccess); ok { - return x.ReloadSuccess - } - } - return nil -} - -func (x *WatchResponse) GetReloadFailed() *ReloadFailed { - if x != nil { - if x, ok := x.Event.(*WatchResponse_ReloadFailed); ok { - return x.ReloadFailed - } - } - return nil -} - -type isWatchResponse_Event interface { - isWatchResponse_Event() -} - -type WatchResponse_ReloadSuccess struct { - ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` -} - -type WatchResponse_ReloadFailed struct { - ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` -} - -func (*WatchResponse_ReloadSuccess) isWatchResponse_Event() {} - -func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} - type ReloadRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` @@ -150,7 +32,7 @@ type ReloadRequest struct { func (x *ReloadRequest) Reset() { *x = ReloadRequest{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -162,7 +44,7 @@ func (x *ReloadRequest) String() string { func (*ReloadRequest) ProtoMessage() {} func (x *ReloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -175,7 +57,7 @@ func (x *ReloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadRequest.ProtoReflect.Descriptor instead. func (*ReloadRequest) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{0} } func (x *ReloadRequest) GetForce() bool { @@ -198,7 +80,7 @@ type ReloadResponse struct { func (x *ReloadResponse) Reset() { *x = ReloadResponse{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -210,7 +92,7 @@ func (x *ReloadResponse) String() string { func (*ReloadResponse) ProtoMessage() {} func (x *ReloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -223,7 +105,7 @@ func (x *ReloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadResponse.ProtoReflect.Descriptor instead. func (*ReloadResponse) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{1} } func (x *ReloadResponse) GetEvent() isReloadResponse_Event { @@ -279,7 +161,7 @@ type ReloadSuccess struct { func (x *ReloadSuccess) Reset() { *x = ReloadSuccess{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -291,7 +173,7 @@ func (x *ReloadSuccess) String() string { func (*ReloadSuccess) ProtoMessage() {} func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -304,7 +186,7 @@ func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. func (*ReloadSuccess) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} } func (x *ReloadSuccess) GetModule() *v1.Module { @@ -331,7 +213,7 @@ type ReloadFailed struct { func (x *ReloadFailed) Reset() { *x = ReloadFailed{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -343,7 +225,7 @@ func (x *ReloadFailed) String() string { func (*ReloadFailed) ProtoMessage() {} func (x *ReloadFailed) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -356,7 +238,7 @@ func (x *ReloadFailed) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. func (*ReloadFailed) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} } func (x *ReloadFailed) GetErrors() *v11.ErrorList { @@ -380,20 +262,7 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x6c, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x74, 0x6c, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0e, 0x0a, 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xbd, 0x01, 0x0a, 0x0d, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, - 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, - 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, - 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x22, 0x25, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, @@ -421,19 +290,13 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x6f, 0x72, 0x73, 0x32, 0xbf, 0x01, 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x03, 0x90, 0x02, 0x01, 0x12, 0x5e, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, - 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, - 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x30, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, + 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, @@ -459,38 +322,32 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData } -var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ - (*WatchRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.WatchRequest - (*WatchResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.WatchResponse - (*ReloadRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadRequest - (*ReloadResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadResponse - (*ReloadSuccess)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess - (*ReloadFailed)(nil), // 5: xyz.block.ftl.hotreload.v1.ReloadFailed - (*v1.Module)(nil), // 6: xyz.block.ftl.schema.v1.Module - (*v11.ErrorList)(nil), // 7: xyz.block.ftl.language.v1.ErrorList - (*v12.PingRequest)(nil), // 8: xyz.block.ftl.v1.PingRequest - (*v12.PingResponse)(nil), // 9: xyz.block.ftl.v1.PingResponse + (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest + (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse + (*ReloadSuccess)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 4: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 5: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 6: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 7: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ - 4, // 0: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 5, // 1: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 4, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 5, // 3: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 6, // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module - 7, // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 7, // 6: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 8, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 0, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest - 2, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest - 9, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 1, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse - 3, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse - 10, // [10:13] is the sub-list for method output_type - 7, // [7:10] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 2, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 3, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 4, // 2: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 5, // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 5, // 4: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 6, // 5: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 6: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 7, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 7, // [7:9] is the sub-list for method output_type + 5, // [5:7] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } @@ -499,10 +356,6 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { return } file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1].OneofWrappers = []any{ - (*WatchResponse_ReloadSuccess)(nil), - (*WatchResponse_ReloadFailed)(nil), - } - file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3].OneofWrappers = []any{ (*ReloadResponse_ReloadSuccess)(nil), (*ReloadResponse_ReloadFailed)(nil), } @@ -512,7 +365,7 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index 882bca0395..2bb109c126 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -9,15 +9,6 @@ import "xyz/block/ftl/v1/ftl.proto"; option go_package = "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb"; option java_multiple_files = true; -message WatchRequest {} - -message WatchResponse { - oneof event { - ReloadSuccess reload_success = 1; - ReloadFailed reload_failed = 2; - } -} - message ReloadRequest { bool force = 1; } @@ -48,9 +39,6 @@ service HotReloadService { option idempotency_level = NO_SIDE_EFFECTS; } - // Watch returns a stream that is notified of any Reloads that are not explicit - rpc Watch(WatchRequest) returns (stream WatchResponse); - // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, // such as when the Reload context changes. // diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go index 9bfeae81cc..779cf9709c 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go @@ -36,8 +36,6 @@ const ( const ( // HotReloadServicePingProcedure is the fully-qualified name of the HotReloadService's Ping RPC. HotReloadServicePingProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Ping" - // HotReloadServiceWatchProcedure is the fully-qualified name of the HotReloadService's Watch RPC. - HotReloadServiceWatchProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Watch" // HotReloadServiceReloadProcedure is the fully-qualified name of the HotReloadService's Reload RPC. HotReloadServiceReloadProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Reload" ) @@ -46,8 +44,6 @@ const ( type HotReloadServiceClient interface { // Ping service for readiness. Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) - // Watch returns a stream that is notified of any Reloads that are not explicit - Watch(context.Context, *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, // such as when the Reload context changes. Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) @@ -69,11 +65,6 @@ func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, op connect.WithIdempotency(connect.IdempotencyNoSideEffects), connect.WithClientOptions(opts...), ), - watch: connect.NewClient[v11.WatchRequest, v11.WatchResponse]( - httpClient, - baseURL+HotReloadServiceWatchProcedure, - opts..., - ), reload: connect.NewClient[v11.ReloadRequest, v11.ReloadResponse]( httpClient, baseURL+HotReloadServiceReloadProcedure, @@ -85,7 +76,6 @@ func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, op // hotReloadServiceClient implements HotReloadServiceClient. type hotReloadServiceClient struct { ping *connect.Client[v1.PingRequest, v1.PingResponse] - watch *connect.Client[v11.WatchRequest, v11.WatchResponse] reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] } @@ -94,11 +84,6 @@ func (c *hotReloadServiceClient) Ping(ctx context.Context, req *connect.Request[ return c.ping.CallUnary(ctx, req) } -// Watch calls xyz.block.ftl.hotreload.v1.HotReloadService.Watch. -func (c *hotReloadServiceClient) Watch(ctx context.Context, req *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) { - return c.watch.CallServerStream(ctx, req) -} - // Reload calls xyz.block.ftl.hotreload.v1.HotReloadService.Reload. func (c *hotReloadServiceClient) Reload(ctx context.Context, req *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { return c.reload.CallUnary(ctx, req) @@ -109,8 +94,6 @@ func (c *hotReloadServiceClient) Reload(ctx context.Context, req *connect.Reques type HotReloadServiceHandler interface { // Ping service for readiness. Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) - // Watch returns a stream that is notified of any Reloads that are not explicit - Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, // such as when the Reload context changes. Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) @@ -128,11 +111,6 @@ func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.Han connect.WithIdempotency(connect.IdempotencyNoSideEffects), connect.WithHandlerOptions(opts...), ) - hotReloadServiceWatchHandler := connect.NewServerStreamHandler( - HotReloadServiceWatchProcedure, - svc.Watch, - opts..., - ) hotReloadServiceReloadHandler := connect.NewUnaryHandler( HotReloadServiceReloadProcedure, svc.Reload, @@ -142,8 +120,6 @@ func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.Han switch r.URL.Path { case HotReloadServicePingProcedure: hotReloadServicePingHandler.ServeHTTP(w, r) - case HotReloadServiceWatchProcedure: - hotReloadServiceWatchHandler.ServeHTTP(w, r) case HotReloadServiceReloadProcedure: hotReloadServiceReloadHandler.ServeHTTP(w, r) default: @@ -159,10 +135,6 @@ func (UnimplementedHotReloadServiceHandler) Ping(context.Context, *connect.Reque return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Ping is not implemented")) } -func (UnimplementedHotReloadServiceHandler) Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error { - return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Watch is not implemented")) -} - func (UnimplementedHotReloadServiceHandler) Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Reload is not implemented")) } diff --git a/internal/buildengine/languageplugin/plugin_client.go b/internal/buildengine/languageplugin/plugin_client.go index 73f6d59299..6550a95ec4 100644 --- a/internal/buildengine/languageplugin/plugin_client.go +++ b/internal/buildengine/languageplugin/plugin_client.go @@ -62,7 +62,7 @@ func (p *pluginClientImpl) start(ctx context.Context, dir, language, name string } envvars := []string{"FTL_NAME=" + name} plugin, cmdCtx, err := plugin.Spawn(ctx, - log.Info, + log.FromContext(ctx).GetLevel(), name, name, dir, diff --git a/internal/buildengine/languageplugin/plugin_integration_test.go b/internal/buildengine/languageplugin/plugin_integration_test.go index 93c79a88e6..0a13b95154 100644 --- a/internal/buildengine/languageplugin/plugin_integration_test.go +++ b/internal/buildengine/languageplugin/plugin_integration_test.go @@ -66,7 +66,7 @@ const ( func TestBuilds(t *testing.T) { sch := generateInitialSchema(t) in.Run(t, - in.WithLanguages("java"), + in.WithLanguages("go"), in.WithoutController(), in.WithoutProvisioner(), in.WithoutTimeline(), @@ -92,7 +92,7 @@ func TestBuilds(t *testing.T) { // Update verb name and expect auto rebuild started and ended modifyVerbName(MODULE_NAME, VERB_NAME_SNIPPET, "aaabbbccc"), - waitForAutoRebuildToStart("build-and-watch"), + in.IfLanguages(waitForAutoRebuildToStart("build-and-watch"), "go"), waitForBuildToEnd(SUCCESS, "build-and-watch", true, func(t testing.TB, ic in.TestContext, event *langpb.BuildResponse) { successEvent, ok := event.Event.(*langpb.BuildResponse_BuildSuccess) assert.True(t, ok) @@ -109,7 +109,7 @@ func TestBuilds(t *testing.T) { // Trigger an auto rebuild, but when we are told of the build being started, send a build context update // to force a new build modifyVerbName(MODULE_NAME, "aaabbbccc", "aaaabbbbcccc"), - waitForAutoRebuildToStart("build-and-watch"), + in.IfLanguages(waitForAutoRebuildToStart("build-and-watch"), "go"), sendUpdatedBuildContext("explicit-build", []string{}, sch), waitForBuildToEnd(SUCCESSORFAILURE, "build-and-watch", true, nil), waitForBuildToEnd(SUCCESS, "explicit-build", false, nil), diff --git a/internal/integration/harness.go b/internal/integration/harness.go index 327f9c89d0..c2497e10af 100644 --- a/internal/integration/harness.go +++ b/internal/integration/harness.go @@ -48,7 +48,7 @@ const dumpPath = "/tmp/ftl-kube-report" var RedPandaBrokers = []string{"127.0.0.1:19092"} func (i TestContext) integrationTestTimeout() time.Duration { - timeout := optional.Zero(os.Getenv("FTL_INTEGRATION_TEST_TIMEOUT")).Default("5s") + timeout := optional.Zero(os.Getenv("FTL_INTEGRATION_TEST_TIMEOUT")).Default("25s") d, err := time.ParseDuration(timeout) if err != nil { panic(err) diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 6e164d0c28..3a10790450 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -383,6 +383,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb command := exec.Command(ctx, log.Debug, buildCtx.Config.Dir, "bash", "-c", devModeBuild) command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind)) command.Env = append(command.Env, fmt.Sprintf("FTL_RUNNER_INFO=%s", runnerInfoFile)) + command.Env = append(command.Env, fmt.Sprintf("QUARKUS_LOG_LEVEL=%s", "DEBUG")) command.Stdout = os.Stdout command.Stderr = os.Stderr err = command.Run() @@ -427,12 +428,18 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } resp := s.toBuildResponse(result.Msg, &bc.buildCtx, false, devModeEndpoint, debugPort32, runnerInfoFile) - err = stream.Send(resp) + if resp != nil { + err = stream.Send(resp) + } if err != nil { return fmt.Errorf("failed to send response %w", err) } case <-schemaChangeTicker.C: + _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) + if err != nil { + return fmt.Errorf("failed to invoke hot reload for build context update %w", err) + } changed := false file, err := os.ReadFile(errorFile) if err == nil { @@ -632,7 +639,6 @@ func (s *Service) BuildContextUpdated(ctx context.Context, req *connect.Request[ if err != nil { return nil, err } - s.writeGenericSchemaFiles(ctx, buildCtx.Schema, buildCtx.Config) err = s.writeGenericSchemaFiles(ctx, buildCtx.Schema, buildCtx.Config) if err != nil { @@ -984,5 +990,5 @@ func (s *Service) toBuildResponse(result *hotreloadpb.ReloadResponse, bc *buildC }, } } - return nil + return ret } From c1c195544d34db65298d49822c87d256b3fb133b Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 22 Jan 2025 09:26:41 +1100 Subject: [PATCH 06/11] fix hot reload --- .gitignore | 1 + common/schema/go2proto.to.go | 19 ++ .../ftl/hotreload/v1/hotreload_connect.ts | 45 +++++ .../block/ftl/hotreload/v1/hotreload_pb.ts | 183 ++++++++++++++++++ .../ftl/deployment/HotReloadHandler.java | 8 +- .../block/ftl/deployment/ModuleBuilder.java | 12 +- .../block/ftl/deployment/ModuleProcessor.java | 15 +- jvm-runtime/plugin/common/jvmcommon.go | 17 +- .../block/ftl/hotreload/v1/hotreload_pb2.py | 50 +++++ .../block/ftl/hotreload/v1/hotreload_pb2.pyi | 36 ++++ 10 files changed, 376 insertions(+), 10 deletions(-) create mode 100644 frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts create mode 100644 frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts create mode 100644 python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py create mode 100644 python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi diff --git a/.gitignore b/.gitignore index ad3a92273f..570d41da2a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ testdata/**/go.work.sum **/smoketest/**/ftl-module-schema/**/* go-runtime/schema/testdata/test/test.go .cache +.ftl-runner-info # Leaving old _ftl for now to avoid old stuff getting checked in **/testdata/**/_ftl diff --git a/common/schema/go2proto.to.go b/common/schema/go2proto.to.go index 4b85344569..59204a5449 100644 --- a/common/schema/go2proto.to.go +++ b/common/schema/go2proto.to.go @@ -1198,9 +1198,13 @@ func MetadataCronJobFromProto(v *destpb.MetadataCronJob) (out *MetadataCronJob, if out.Pos, err = orZeroR(result.From(PositionFromProto(v.Pos))).Result(); err != nil { return nil, fmt.Errorf("Pos: %w", err) } +<<<<<<< HEAD if out.Cron, err = orZeroR(result.From(ptr(string(v.Cron)), nil)).Result(); err != nil { return nil, fmt.Errorf("Cron: %w", err) } +======= + out.Cron = string(v.Cron) +>>>>>>> 505dfe2d4 (fix hot reload) return out, nil } @@ -1386,8 +1390,13 @@ func (x *MetadataSQLColumn) ToProto() *destpb.MetadataSQLColumn { } return &destpb.MetadataSQLColumn{ Pos: x.Pos.ToProto(), +<<<<<<< HEAD Table: orZero(ptr(string(x.Table))), Name: orZero(ptr(string(x.Name))), +======= + Table: string(x.Table), + Name: string(x.Name), +>>>>>>> 505dfe2d4 (fix hot reload) } } @@ -1397,6 +1406,7 @@ func MetadataSQLColumnFromProto(v *destpb.MetadataSQLColumn) (out *MetadataSQLCo } out = &MetadataSQLColumn{} +<<<<<<< HEAD if out.Pos, err = orZeroR(result.From(PositionFromProto(v.Pos))).Result(); err != nil { return nil, fmt.Errorf("Pos: %w", err) } @@ -1406,6 +1416,15 @@ func MetadataSQLColumnFromProto(v *destpb.MetadataSQLColumn) (out *MetadataSQLCo if out.Name, err = orZeroR(result.From(ptr(string(v.Name)), nil)).Result(); err != nil { return nil, fmt.Errorf("Name: %w", err) } +======= + if fieldPos, err := PositionFromProto(v.Pos); err != nil { + return nil, fmt.Errorf("Pos: %w", err) + } else { + out.Pos = fromPtr(fieldPos) + } + out.Table = string(v.Table) + out.Name = string(v.Name) +>>>>>>> 505dfe2d4 (fix hot reload) return out, nil } diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts new file mode 100644 index 0000000000..89196e8466 --- /dev/null +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts @@ -0,0 +1,45 @@ +// @generated by protoc-gen-connect-es v1.6.1 with parameter "target=ts" +// @generated from file xyz/block/ftl/hotreload/v1/hotreload.proto (package xyz.block.ftl.hotreload.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { PingRequest, PingResponse } from "../../v1/ftl_pb.js"; +import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; +import { ReloadRequest, ReloadResponse } from "./hotreload_pb.js"; + +/** + * HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload + * + * @generated from service xyz.block.ftl.hotreload.v1.HotReloadService + */ +export const HotReloadService = { + typeName: "xyz.block.ftl.hotreload.v1.HotReloadService", + methods: { + /** + * Ping service for readiness. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Ping + */ + ping: { + name: "Ping", + I: PingRequest, + O: PingResponse, + kind: MethodKind.Unary, + idempotency: MethodIdempotency.NoSideEffects, + }, + /** + * Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + * such as when the Reload context changes. + * + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Reload + */ + reload: { + name: "Reload", + I: ReloadRequest, + O: ReloadResponse, + kind: MethodKind.Unary, + }, + } +} as const; + diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts new file mode 100644 index 0000000000..03db7ce24c --- /dev/null +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts @@ -0,0 +1,183 @@ +// @generated by protoc-gen-es v1.10.0 with parameter "target=ts" +// @generated from file xyz/block/ftl/hotreload/v1/hotreload.proto (package xyz.block.ftl.hotreload.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import { Module } from "../../schema/v1/schema_pb.js"; +import { ErrorList } from "../../language/v1/language_pb.js"; + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadRequest + */ +export class ReloadRequest extends Message { + /** + * @generated from field: bool force = 1; + */ + force = false; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "force", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadRequest { + return new ReloadRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadRequest { + return new ReloadRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadRequest { + return new ReloadRequest().fromJsonString(jsonString, options); + } + + static equals(a: ReloadRequest | PlainMessage | undefined, b: ReloadRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadResponse + */ +export class ReloadResponse extends Message { + /** + * @generated from oneof xyz.block.ftl.hotreload.v1.ReloadResponse.event + */ + event: { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 1; + */ + value: ReloadSuccess; + case: "reloadSuccess"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 2; + */ + value: ReloadFailed; + case: "reloadFailed"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, + { no: 2, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadResponse { + return new ReloadResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadResponse { + return new ReloadResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadResponse { + return new ReloadResponse().fromJsonString(jsonString, options); + } + + static equals(a: ReloadResponse | PlainMessage | undefined, b: ReloadResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadResponse, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadSuccess + */ +export class ReloadSuccess extends Message { + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.schema.v1.Module module = 1; + */ + module?: Module; + + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.language.v1.ErrorList errors = 2; + */ + errors?: ErrorList; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadSuccess"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "module", kind: "message", T: Module }, + { no: 2, name: "errors", kind: "message", T: ErrorList }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromJsonString(jsonString, options); + } + + static equals(a: ReloadSuccess | PlainMessage | undefined, b: ReloadSuccess | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadSuccess, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadFailed + */ +export class ReloadFailed extends Message { + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.language.v1.ErrorList errors = 1; + */ + errors?: ErrorList; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadFailed"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "errors", kind: "message", T: ErrorList }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadFailed { + return new ReloadFailed().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadFailed { + return new ReloadFailed().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadFailed { + return new ReloadFailed().fromJsonString(jsonString, options); + } + + static equals(a: ReloadFailed | PlainMessage | undefined, b: ReloadFailed | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadFailed, a, b); + } +} + diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index d7524dac7c..8c7491adf3 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -33,6 +33,7 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); static volatile Module module; + static volatile ErrorList errors; private static final AtomicBoolean started = new AtomicBoolean(); private static volatile Server server; @@ -44,6 +45,7 @@ public void ping(PingRequest request, StreamObserver responseObser @Override public void reload(ReloadRequest request, StreamObserver responseObserver) { + doScan(request.getForce()); Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); @@ -67,7 +69,11 @@ public void reload(ReloadRequest request, StreamObserver respons .setReloadFailed(ReloadFailed.newBuilder() .setErrors(builder).build()) .build()); - responseObserver.onCompleted(); + } else if (errors != null && errors.getErrorsCount() > 0) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(errors).build()) + .build()); } else if (module != null) { responseObserver.onNext(ReloadResponse.newBuilder() .setReloadSuccess(ReloadSuccess.newBuilder() diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java index 28d60b9028..77941bee9e 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java @@ -17,6 +17,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -537,7 +538,7 @@ public int getDeclsCount() { return decls.size(); } - public void writeTo(OutputStream out, OutputStream errorOut) throws IOException { + public void writeTo(OutputStream out, OutputStream errorOut, BiConsumer consumer) throws IOException { decls.values().stream().forEachOrdered(protoModuleBuilder::addDecls); ErrorList.Builder builder = ErrorList.newBuilder(); if (!validationFailures.isEmpty()) { @@ -552,8 +553,13 @@ public void writeTo(OutputStream out, OutputStream errorOut) throws IOException .build())); errors.forEach(log::error); } - builder.build().writeTo(errorOut); - protoModuleBuilder.build().writeTo(out); + ErrorList errorList = builder.build(); + errorList.writeTo(errorOut); + Module module = protoModuleBuilder.build(); + module.writeTo(out); + if (consumer != null) { + consumer.accept(module, errorList); + } } public void registerTypeAlias(String name, org.jboss.jandex.Type finalT, org.jboss.jandex.Type finalS, boolean exported, diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index a4a6ac2a5c..251f1cde3b 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import org.jboss.jandex.DotName; @@ -42,6 +44,7 @@ import io.quarkus.runtime.util.HashUtil; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; +import xyz.block.ftl.language.v1.ErrorList; import xyz.block.ftl.runtime.FTLDatasourceCredentials; import xyz.block.ftl.runtime.FTLRecorder; import xyz.block.ftl.runtime.JsonSerializationConfig; @@ -168,7 +171,15 @@ public void generateSchema(CombinedIndexBuildItem index, Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); ByteArrayOutputStream sch = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); - moduleBuilder.writeTo(sch, err); + AtomicReference errRef = new AtomicReference<>(); + AtomicReference schRef = new AtomicReference<>(); + moduleBuilder.writeTo(sch, err, new BiConsumer() { + @Override + public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { + errRef.set(errorList); + schRef.set(module); + } + }); var schBytes = sch.toByteArray(); var errBytes = err.toByteArray(); @@ -178,6 +189,8 @@ public void generateSchema(CombinedIndexBuildItem index, Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { + HotReloadHandler.module =schRef.get(); + HotReloadHandler.errors = errRef.get(); // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. var hash = HashUtil.sha256(schBytes); diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 3a10790450..be05279ff5 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -383,7 +383,6 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb command := exec.Command(ctx, log.Debug, buildCtx.Config.Dir, "bash", "-c", devModeBuild) command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind)) command.Env = append(command.Env, fmt.Sprintf("FTL_RUNNER_INFO=%s", runnerInfoFile)) - command.Env = append(command.Env, fmt.Sprintf("QUARKUS_LOG_LEVEL=%s", "DEBUG")) command.Stdout = os.Stdout command.Stderr = os.Stderr err = command.Run() @@ -400,8 +399,10 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, fmt.Sprintf("http://localhost:%d", protoPort.Port), log.Trace) err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) if err != nil { + logger.Infof("Dev mode process failed to start") return fmt.Errorf("timed out waiting for start %w", err) } + logger.Infof("Dev mode process started") schemaChangeTicker := time.NewTicker(500 * time.Millisecond) defer schemaChangeTicker.Stop() @@ -435,10 +436,13 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb return fmt.Errorf("failed to send response %w", err) } case <-schemaChangeTicker.C: - - _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) - if err != nil { - return fmt.Errorf("failed to invoke hot reload for build context update %w", err) + if !firstAttempt { + logger.Infof("Checking for schema changes") + _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) + if err != nil { + logger.Errorf(err, "Failed to invoke hot reload for build context update") + return fmt.Errorf("failed to invoke hot reload for build context update %w", err) + } } changed := false file, err := os.ReadFile(errorFile) @@ -468,8 +472,10 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } } if changed || forceUpdate { + logger.Infof("updating") auto := !firstAttempt && !forceUpdate if auto { + logger.Infof("sending auto") err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_AutoRebuildStarted{AutoRebuildStarted: &langpb.AutoRebuildStarted{ContextId: buildCtx.ID}}}) if err != nil { return fmt.Errorf("could not send build event: %w", err) @@ -482,6 +488,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } if builderrors.ContainsTerminalError(langpb.ErrorsFromProto(buildErrs)) { + logger.Infof("terminal errors") // skip reading schema err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_BuildFailure{ BuildFailure: &langpb.BuildFailure{ diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py new file mode 100644 index 0000000000..91c97807aa --- /dev/null +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: xyz/block/ftl/hotreload/v1/hotreload.proto +# Protobuf Python Version: 5.29.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 2, + '', + 'xyz/block/ftl/hotreload/v1/hotreload.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from xyz.block.ftl.language.v1 import language_pb2 as xyz_dot_block_dot_ftl_dot_language_dot_v1_dot_language__pb2 +from xyz.block.ftl.schema.v1 import schema_pb2 as xyz_dot_block_dot_ftl_dot_schema_dot_v1_dot_schema__pb2 +from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\xbe\x01\n\x0eReloadResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\xbf\x01\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'xyz.block.ftl.hotreload.v1.hotreload_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'P\001ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb' + _globals['_HOTRELOADSERVICE'].methods_by_name['Ping']._loaded_options = None + _globals['_HOTRELOADSERVICE'].methods_by_name['Ping']._serialized_options = b'\220\002\001' + _globals['_RELOADREQUEST']._serialized_start=182 + _globals['_RELOADREQUEST']._serialized_end=219 + _globals['_RELOADRESPONSE']._serialized_start=222 + _globals['_RELOADRESPONSE']._serialized_end=412 + _globals['_RELOADSUCCESS']._serialized_start=415 + _globals['_RELOADSUCCESS']._serialized_end=549 + _globals['_RELOADFAILED']._serialized_start=551 + _globals['_RELOADFAILED']._serialized_end=627 + _globals['_HOTRELOADSERVICE']._serialized_start=630 + _globals['_HOTRELOADSERVICE']._serialized_end=821 +# @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi new file mode 100644 index 0000000000..e45b3e609d --- /dev/null +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi @@ -0,0 +1,36 @@ +from xyz.block.ftl.language.v1 import language_pb2 as _language_pb2 +from xyz.block.ftl.schema.v1 import schema_pb2 as _schema_pb2 +from xyz.block.ftl.v1 import ftl_pb2 as _ftl_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ReloadRequest(_message.Message): + __slots__ = ("force",) + FORCE_FIELD_NUMBER: _ClassVar[int] + force: bool + def __init__(self, force: bool = ...) -> None: ... + +class ReloadResponse(_message.Message): + __slots__ = ("reload_success", "reload_failed") + RELOAD_SUCCESS_FIELD_NUMBER: _ClassVar[int] + RELOAD_FAILED_FIELD_NUMBER: _ClassVar[int] + reload_success: ReloadSuccess + reload_failed: ReloadFailed + def __init__(self, reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + +class ReloadSuccess(_message.Message): + __slots__ = ("module", "errors") + MODULE_FIELD_NUMBER: _ClassVar[int] + ERRORS_FIELD_NUMBER: _ClassVar[int] + module: _schema_pb2.Module + errors: _language_pb2.ErrorList + def __init__(self, module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., errors: _Optional[_Union[_language_pb2.ErrorList, _Mapping]] = ...) -> None: ... + +class ReloadFailed(_message.Message): + __slots__ = ("errors",) + ERRORS_FIELD_NUMBER: _ClassVar[int] + errors: _language_pb2.ErrorList + def __init__(self, errors: _Optional[_Union[_language_pb2.ErrorList, _Mapping]] = ...) -> None: ... From ef4d321e720591cae1ff13cee0bc48fdf605bfda Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 22 Jan 2025 09:58:11 +1100 Subject: [PATCH 07/11] fixes --- .../xyz/block/ftl/deployment/HotReloadHandler.java | 3 +++ .../java/xyz/block/ftl/deployment/ModuleProcessor.java | 2 +- jvm-runtime/plugin/common/jvmcommon.go | 10 ++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index 8c7491adf3..8654f021b5 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -69,16 +69,19 @@ public void reload(ReloadRequest request, StreamObserver respons .setReloadFailed(ReloadFailed.newBuilder() .setErrors(builder).build()) .build()); + responseObserver.onCompleted(); } else if (errors != null && errors.getErrorsCount() > 0) { responseObserver.onNext(ReloadResponse.newBuilder() .setReloadFailed(ReloadFailed.newBuilder() .setErrors(errors).build()) .build()); + responseObserver.onCompleted(); } else if (module != null) { responseObserver.onNext(ReloadResponse.newBuilder() .setReloadSuccess(ReloadSuccess.newBuilder() .setModule(module).build()) .build()); + responseObserver.onCompleted(); } else { responseObserver.onError(new RuntimeException("schema not generated")); } diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 251f1cde3b..7d1ec3ec01 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -189,7 +189,7 @@ public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { - HotReloadHandler.module =schRef.get(); + HotReloadHandler.module = schRef.get(); HotReloadHandler.errors = errRef.get(); // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index be05279ff5..81f44e3c7b 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -409,6 +409,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb for { select { case <-ctx.Done(): + logger.Debugf("Context done") // the context is done before we notified the build engine // we need to send a build failure event err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_BuildFailure{ @@ -422,6 +423,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } return nil case bc := <-events: + logger.Debugf("Build context updated") buildCtx = bc.buildCtx forceUpdate = true result, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: true})) @@ -437,10 +439,8 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } case <-schemaChangeTicker.C: if !firstAttempt { - logger.Infof("Checking for schema changes") _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) if err != nil { - logger.Errorf(err, "Failed to invoke hot reload for build context update") return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } } @@ -472,10 +472,9 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } } if changed || forceUpdate { - logger.Infof("updating") auto := !firstAttempt && !forceUpdate if auto { - logger.Infof("sending auto") + logger.Debugf("sending auto") err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_AutoRebuildStarted{AutoRebuildStarted: &langpb.AutoRebuildStarted{ContextId: buildCtx.ID}}}) if err != nil { return fmt.Errorf("could not send build event: %w", err) @@ -488,7 +487,6 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } if builderrors.ContainsTerminalError(langpb.ErrorsFromProto(buildErrs)) { - logger.Infof("terminal errors") // skip reading schema err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_BuildFailure{ BuildFailure: &langpb.BuildFailure{ @@ -510,7 +508,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } - logger.Infof("Live reload schema changed, sending build success event") + logger.Debugf("Live reload schema changed, sending build success event") err = stream.Send(&langpb.BuildResponse{ Event: &langpb.BuildResponse_BuildSuccess{ BuildSuccess: &langpb.BuildSuccess{ From e4b250570b07fe5b477e43d1b37c2233d23b5963 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 22 Jan 2025 14:28:09 +1100 Subject: [PATCH 08/11] tmp --- .../block/ftl/hotreload/v1/hotreload.pb.go | 215 +++++++++++------- .../block/ftl/hotreload/v1/hotreload.proto | 7 +- common/schema/go2proto.to.go | 19 -- .../block/ftl/hotreload/v1/hotreload_pb.ts | 46 +++- .../ftl/deployment/HotReloadHandler.java | 53 ++++- .../block/ftl/deployment/ModuleProcessor.java | 3 +- jvm-runtime/plugin/common/jvmcommon.go | 6 +- .../block/ftl/hotreload/v1/hotreload_pb2.py | 18 +- .../block/ftl/hotreload/v1/hotreload_pb2.pyi | 10 +- 9 files changed, 254 insertions(+), 123 deletions(-) diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go index 1b19c527a7..7929ff79a4 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -71,6 +71,7 @@ type ReloadResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Event: // + // *ReloadResponse_ReloadNotRequired // *ReloadResponse_ReloadSuccess // *ReloadResponse_ReloadFailed Event isReloadResponse_Event `protobuf_oneof:"event"` @@ -115,6 +116,15 @@ func (x *ReloadResponse) GetEvent() isReloadResponse_Event { return nil } +func (x *ReloadResponse) GetReloadNotRequired() *ReloadNotRequired { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadNotRequired); ok { + return x.ReloadNotRequired + } + } + return nil +} + func (x *ReloadResponse) GetReloadSuccess() *ReloadSuccess { if x != nil { if x, ok := x.Event.(*ReloadResponse_ReloadSuccess); ok { @@ -137,18 +147,60 @@ type isReloadResponse_Event interface { isReloadResponse_Event() } +type ReloadResponse_ReloadNotRequired struct { + ReloadNotRequired *ReloadNotRequired `protobuf:"bytes,1,opt,name=reload_not_required,json=reloadNotRequired,proto3,oneof"` +} + type ReloadResponse_ReloadSuccess struct { - ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` + ReloadSuccess *ReloadSuccess `protobuf:"bytes,2,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` } type ReloadResponse_ReloadFailed struct { - ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` + ReloadFailed *ReloadFailed `protobuf:"bytes,3,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` } +func (*ReloadResponse_ReloadNotRequired) isReloadResponse_Event() {} + func (*ReloadResponse_ReloadSuccess) isReloadResponse_Event() {} func (*ReloadResponse_ReloadFailed) isReloadResponse_Event() {} +type ReloadNotRequired struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadNotRequired) Reset() { + *x = ReloadNotRequired{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadNotRequired) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadNotRequired) ProtoMessage() {} + +func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadNotRequired.ProtoReflect.Descriptor instead. +func (*ReloadNotRequired) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} +} + type ReloadSuccess struct { state protoimpl.MessageState `protogen:"open.v1"` // Module schema for the built module @@ -161,7 +213,7 @@ type ReloadSuccess struct { func (x *ReloadSuccess) Reset() { *x = ReloadSuccess{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -173,7 +225,7 @@ func (x *ReloadSuccess) String() string { func (*ReloadSuccess) ProtoMessage() {} func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -186,7 +238,7 @@ func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. func (*ReloadSuccess) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} } func (x *ReloadSuccess) GetModule() *v1.Module { @@ -213,7 +265,7 @@ type ReloadFailed struct { func (x *ReloadFailed) Reset() { *x = ReloadFailed{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -225,7 +277,7 @@ func (x *ReloadFailed) String() string { func (*ReloadFailed) ProtoMessage() {} func (x *ReloadFailed) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -238,7 +290,7 @@ func (x *ReloadFailed) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. func (*ReloadFailed) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} } func (x *ReloadFailed) GetErrors() *v11.ErrorList { @@ -264,50 +316,58 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x74, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0e, - 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, - 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, 0x69, - 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, - 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, - 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x86, 0x01, 0x0a, - 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, - 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x73, 0x32, 0xbf, 0x01, 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, - 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, - 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, - 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, - 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, - 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x9f, 0x02, 0x0a, 0x0e, + 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, + 0x0a, 0x13, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, + 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x48, 0x00, 0x52, 0x11, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x13, 0x0a, + 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3c, 0x0a, + 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x0c, 0x52, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0xbf, 0x01, 0x0a, 0x10, 0x48, 0x6f, + 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, + 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, 0x5a, + 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, + 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x3b, + 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -322,32 +382,34 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData } -var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ - (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest - (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse - (*ReloadSuccess)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadSuccess - (*ReloadFailed)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadFailed - (*v1.Module)(nil), // 4: xyz.block.ftl.schema.v1.Module - (*v11.ErrorList)(nil), // 5: xyz.block.ftl.language.v1.ErrorList - (*v12.PingRequest)(nil), // 6: xyz.block.ftl.v1.PingRequest - (*v12.PingResponse)(nil), // 7: xyz.block.ftl.v1.PingResponse + (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest + (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse + (*ReloadNotRequired)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadNotRequired + (*ReloadSuccess)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 5: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 6: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 7: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 8: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ - 2, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 3, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 4, // 2: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module - 5, // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 5, // 4: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 6, // 5: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 0, // 6: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest - 7, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 1, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse - 7, // [7:9] is the sub-list for method output_type - 5, // [5:7] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 2, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired + 3, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 4, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 5, // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 6, // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 6, // 5: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 7, // 6: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 8, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 8, // [8:10] is the sub-list for method output_type + 6, // [6:8] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } @@ -356,6 +418,7 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { return } file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1].OneofWrappers = []any{ + (*ReloadResponse_ReloadNotRequired)(nil), (*ReloadResponse_ReloadSuccess)(nil), (*ReloadResponse_ReloadFailed)(nil), } @@ -365,7 +428,7 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index 2bb109c126..86170fc8f1 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -15,11 +15,14 @@ message ReloadRequest { message ReloadResponse { oneof event { - ReloadSuccess reload_success = 1; - ReloadFailed reload_failed = 2; + ReloadNotRequired reload_not_required = 1; + ReloadSuccess reload_success = 2; + ReloadFailed reload_failed = 3; } } +message ReloadNotRequired {} + message ReloadSuccess { // Module schema for the built module ftl.schema.v1.Module module = 1; diff --git a/common/schema/go2proto.to.go b/common/schema/go2proto.to.go index 59204a5449..4b85344569 100644 --- a/common/schema/go2proto.to.go +++ b/common/schema/go2proto.to.go @@ -1198,13 +1198,9 @@ func MetadataCronJobFromProto(v *destpb.MetadataCronJob) (out *MetadataCronJob, if out.Pos, err = orZeroR(result.From(PositionFromProto(v.Pos))).Result(); err != nil { return nil, fmt.Errorf("Pos: %w", err) } -<<<<<<< HEAD if out.Cron, err = orZeroR(result.From(ptr(string(v.Cron)), nil)).Result(); err != nil { return nil, fmt.Errorf("Cron: %w", err) } -======= - out.Cron = string(v.Cron) ->>>>>>> 505dfe2d4 (fix hot reload) return out, nil } @@ -1390,13 +1386,8 @@ func (x *MetadataSQLColumn) ToProto() *destpb.MetadataSQLColumn { } return &destpb.MetadataSQLColumn{ Pos: x.Pos.ToProto(), -<<<<<<< HEAD Table: orZero(ptr(string(x.Table))), Name: orZero(ptr(string(x.Name))), -======= - Table: string(x.Table), - Name: string(x.Name), ->>>>>>> 505dfe2d4 (fix hot reload) } } @@ -1406,7 +1397,6 @@ func MetadataSQLColumnFromProto(v *destpb.MetadataSQLColumn) (out *MetadataSQLCo } out = &MetadataSQLColumn{} -<<<<<<< HEAD if out.Pos, err = orZeroR(result.From(PositionFromProto(v.Pos))).Result(); err != nil { return nil, fmt.Errorf("Pos: %w", err) } @@ -1416,15 +1406,6 @@ func MetadataSQLColumnFromProto(v *destpb.MetadataSQLColumn) (out *MetadataSQLCo if out.Name, err = orZeroR(result.From(ptr(string(v.Name)), nil)).Result(); err != nil { return nil, fmt.Errorf("Name: %w", err) } -======= - if fieldPos, err := PositionFromProto(v.Pos); err != nil { - return nil, fmt.Errorf("Pos: %w", err) - } else { - out.Pos = fromPtr(fieldPos) - } - out.Table = string(v.Table) - out.Name = string(v.Name) ->>>>>>> 505dfe2d4 (fix hot reload) return out, nil } diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts index 03db7ce24c..fd25f17905 100644 --- a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts @@ -54,13 +54,19 @@ export class ReloadResponse extends Message { */ event: { /** - * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 1; + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadNotRequired reload_not_required = 1; + */ + value: ReloadNotRequired; + case: "reloadNotRequired"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 2; */ value: ReloadSuccess; case: "reloadSuccess"; } | { /** - * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 2; + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 3; */ value: ReloadFailed; case: "reloadFailed"; @@ -74,8 +80,9 @@ export class ReloadResponse extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadResponse"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, - { no: 2, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, + { no: 1, name: "reload_not_required", kind: "message", T: ReloadNotRequired, oneof: "event" }, + { no: 2, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, + { no: 3, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ReloadResponse { @@ -95,6 +102,37 @@ export class ReloadResponse extends Message { } } +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadNotRequired + */ +export class ReloadNotRequired extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadNotRequired"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromJsonString(jsonString, options); + } + + static equals(a: ReloadNotRequired | PlainMessage | undefined, b: ReloadNotRequired | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadNotRequired, a, b); + } +} + /** * @generated from message xyz.block.ftl.hotreload.v1.ReloadSuccess */ diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index 8654f021b5..dd8845dbf6 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -17,6 +17,7 @@ import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import xyz.block.ftl.hotreload.v1.HotReloadServiceGrpc; import xyz.block.ftl.hotreload.v1.ReloadFailed; +import xyz.block.ftl.hotreload.v1.ReloadNotRequired; import xyz.block.ftl.hotreload.v1.ReloadRequest; import xyz.block.ftl.hotreload.v1.ReloadResponse; import xyz.block.ftl.hotreload.v1.ReloadSuccess; @@ -32,10 +33,18 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); - static volatile Module module; - static volatile ErrorList errors; + private static volatile Module module; + private static volatile ErrorList errors; private static final AtomicBoolean started = new AtomicBoolean(); private static volatile Server server; + private static volatile boolean restarting = false; + + synchronized static void setResults(Module module, ErrorList errors) { + HotReloadHandler.module = module; + HotReloadHandler.errors = errors; + restarting = false; + HotReloadHandler.class.notifyAll(); + } @Override public void ping(PingRequest request, StreamObserver responseObserver) { @@ -46,7 +55,31 @@ public void ping(PingRequest request, StreamObserver responseObser @Override public void reload(ReloadRequest request, StreamObserver responseObserver) { - doScan(request.getForce()); + // This is complex, as the restart can't happen until the runner is up + // We want to report on the results of the schema generations, so we can bring up a runner + // Run the restart in a new thread, so we can report on the schema once it is ready + var currentModule = module; + Thread t = new Thread(() -> { + try { + doScan(request.getForce()); + } finally { + synchronized (HotReloadHandler.class) { + restarting = false; + HotReloadHandler.class.notifyAll(); + } + } + }, "FTL Restart Thread"); + synchronized (HotReloadHandler.class) { + restarting = true; + t.start(); + while (restarting) { + try { + HotReloadHandler.class.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); if (compileProblem != null || deploymentProblems != null) { @@ -77,10 +110,16 @@ public void reload(ReloadRequest request, StreamObserver respons .build()); responseObserver.onCompleted(); } else if (module != null) { - responseObserver.onNext(ReloadResponse.newBuilder() - .setReloadSuccess(ReloadSuccess.newBuilder() - .setModule(module).build()) - .build()); + if (module == currentModule) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) + .build()); + } else { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder() + .setModule(module).build()) + .build()); + } responseObserver.onCompleted(); } else { responseObserver.onError(new RuntimeException("schema not generated")); diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 7d1ec3ec01..69b2f4b53c 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -189,8 +189,7 @@ public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { - HotReloadHandler.module = schRef.get(); - HotReloadHandler.errors = errRef.get(); + HotReloadHandler.setResults(schRef.get(), errRef.get()); // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. var hash = HashUtil.sha256(schBytes); diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 81f44e3c7b..b8fe5ba202 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -438,13 +438,13 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb return fmt.Errorf("failed to send response %w", err) } case <-schemaChangeTicker.C: + changed := false if !firstAttempt { _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) if err != nil { return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } } - changed := false file, err := os.ReadFile(errorFile) if err == nil { sum := sha256.Sum(file) @@ -474,7 +474,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if changed || forceUpdate { auto := !firstAttempt && !forceUpdate if auto { - logger.Debugf("sending auto") + logger.Infof("sending auto") err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_AutoRebuildStarted{AutoRebuildStarted: &langpb.AutoRebuildStarted{ContextId: buildCtx.ID}}}) if err != nil { return fmt.Errorf("could not send build event: %w", err) @@ -508,7 +508,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } - logger.Debugf("Live reload schema changed, sending build success event") + logger.Infof("Live reload schema changed, sending build success event") err = stream.Send(&langpb.BuildResponse{ Event: &langpb.BuildResponse_BuildSuccess{ BuildSuccess: &langpb.BuildSuccess{ diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py index 91c97807aa..c4b9edad86 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\xbe\x01\n\x0eReloadResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\xbf\x01\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\xbf\x01\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -40,11 +40,13 @@ _globals['_RELOADREQUEST']._serialized_start=182 _globals['_RELOADREQUEST']._serialized_end=219 _globals['_RELOADRESPONSE']._serialized_start=222 - _globals['_RELOADRESPONSE']._serialized_end=412 - _globals['_RELOADSUCCESS']._serialized_start=415 - _globals['_RELOADSUCCESS']._serialized_end=549 - _globals['_RELOADFAILED']._serialized_start=551 - _globals['_RELOADFAILED']._serialized_end=627 - _globals['_HOTRELOADSERVICE']._serialized_start=630 - _globals['_HOTRELOADSERVICE']._serialized_end=821 + _globals['_RELOADRESPONSE']._serialized_end=509 + _globals['_RELOADNOTREQUIRED']._serialized_start=511 + _globals['_RELOADNOTREQUIRED']._serialized_end=530 + _globals['_RELOADSUCCESS']._serialized_start=533 + _globals['_RELOADSUCCESS']._serialized_end=667 + _globals['_RELOADFAILED']._serialized_start=669 + _globals['_RELOADFAILED']._serialized_end=745 + _globals['_HOTRELOADSERVICE']._serialized_start=748 + _globals['_HOTRELOADSERVICE']._serialized_end=939 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi index e45b3e609d..3a9adc5551 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi @@ -14,12 +14,18 @@ class ReloadRequest(_message.Message): def __init__(self, force: bool = ...) -> None: ... class ReloadResponse(_message.Message): - __slots__ = ("reload_success", "reload_failed") + __slots__ = ("reload_not_required", "reload_success", "reload_failed") + RELOAD_NOT_REQUIRED_FIELD_NUMBER: _ClassVar[int] RELOAD_SUCCESS_FIELD_NUMBER: _ClassVar[int] RELOAD_FAILED_FIELD_NUMBER: _ClassVar[int] + reload_not_required: ReloadNotRequired reload_success: ReloadSuccess reload_failed: ReloadFailed - def __init__(self, reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + def __init__(self, reload_not_required: _Optional[_Union[ReloadNotRequired, _Mapping]] = ..., reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + +class ReloadNotRequired(_message.Message): + __slots__ = () + def __init__(self) -> None: ... class ReloadSuccess(_message.Message): __slots__ = ("module", "errors") From fca5b79362e23aed9faf65034be9922152a4fa33 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 23 Jan 2025 10:09:20 +1100 Subject: [PATCH 09/11] add watch function --- .../block/ftl/hotreload/v1/hotreload.pb.go | 217 +++++++++++++++--- .../block/ftl/hotreload/v1/hotreload.proto | 15 ++ .../hotreloadpbconnect/hotreload.connect.go | 28 +++ .../ftl/hotreload/v1/hotreload_connect.ts | 13 +- .../block/ftl/hotreload/v1/hotreload_pb.ts | 81 +++++++ .../ftl/deployment/HotReloadHandler.java | 108 ++++++--- .../block/ftl/deployment/ModuleProcessor.java | 4 +- .../block/ftl/hotreload/v1/hotreload_pb2.py | 22 +- .../block/ftl/hotreload/v1/hotreload_pb2.pyi | 12 + 9 files changed, 425 insertions(+), 75 deletions(-) diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go index 7929ff79a4..1f1dbf8543 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -165,6 +165,124 @@ func (*ReloadResponse_ReloadSuccess) isReloadResponse_Event() {} func (*ReloadResponse_ReloadFailed) isReloadResponse_Event() {} +type WatchRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchRequest) Reset() { + *x = WatchRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchRequest) ProtoMessage() {} + +func (x *WatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead. +func (*WatchRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} +} + +type WatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *WatchResponse_ReloadSuccess + // *WatchResponse_ReloadFailed + Event isWatchResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchResponse) Reset() { + *x = WatchResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchResponse) ProtoMessage() {} + +func (x *WatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead. +func (*WatchResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} +} + +func (x *WatchResponse) GetEvent() isWatchResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *WatchResponse) GetReloadSuccess() *ReloadSuccess { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadSuccess); ok { + return x.ReloadSuccess + } + } + return nil +} + +func (x *WatchResponse) GetReloadFailed() *ReloadFailed { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadFailed); ok { + return x.ReloadFailed + } + } + return nil +} + +type isWatchResponse_Event interface { + isWatchResponse_Event() +} + +type WatchResponse_ReloadSuccess struct { + ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` +} + +type WatchResponse_ReloadFailed struct { + ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` +} + +func (*WatchResponse_ReloadSuccess) isWatchResponse_Event() {} + +func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} + type ReloadNotRequired struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -173,7 +291,7 @@ type ReloadNotRequired struct { func (x *ReloadNotRequired) Reset() { *x = ReloadNotRequired{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -185,7 +303,7 @@ func (x *ReloadNotRequired) String() string { func (*ReloadNotRequired) ProtoMessage() {} func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -198,7 +316,7 @@ func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadNotRequired.ProtoReflect.Descriptor instead. func (*ReloadNotRequired) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} } type ReloadSuccess struct { @@ -213,7 +331,7 @@ type ReloadSuccess struct { func (x *ReloadSuccess) Reset() { *x = ReloadSuccess{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -225,7 +343,7 @@ func (x *ReloadSuccess) String() string { func (*ReloadSuccess) ProtoMessage() {} func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -238,7 +356,7 @@ func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. func (*ReloadSuccess) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} } func (x *ReloadSuccess) GetModule() *v1.Module { @@ -265,7 +383,7 @@ type ReloadFailed struct { func (x *ReloadFailed) Reset() { *x = ReloadFailed{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -277,7 +395,7 @@ func (x *ReloadFailed) String() string { func (*ReloadFailed) ProtoMessage() {} func (x *ReloadFailed) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -290,7 +408,7 @@ func (x *ReloadFailed) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. func (*ReloadFailed) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{6} } func (x *ReloadFailed) GetErrors() *v11.ErrorList { @@ -334,6 +452,19 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x0e, 0x0a, + 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbd, 0x01, + 0x0a, 0x0d, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, @@ -349,7 +480,7 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0xbf, 0x01, 0x0a, 0x10, 0x48, 0x6f, + 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, @@ -361,7 +492,13 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, 0x5a, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x05, 0x57, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, + 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x4e, 0x50, 0x01, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, @@ -382,34 +519,40 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData } -var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse - (*ReloadNotRequired)(nil), // 2: xyz.block.ftl.hotreload.v1.ReloadNotRequired - (*ReloadSuccess)(nil), // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess - (*ReloadFailed)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadFailed - (*v1.Module)(nil), // 5: xyz.block.ftl.schema.v1.Module - (*v11.ErrorList)(nil), // 6: xyz.block.ftl.language.v1.ErrorList - (*v12.PingRequest)(nil), // 7: xyz.block.ftl.v1.PingRequest - (*v12.PingResponse)(nil), // 8: xyz.block.ftl.v1.PingResponse + (*WatchRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.WatchRequest + (*WatchResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.WatchResponse + (*ReloadNotRequired)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadNotRequired + (*ReloadSuccess)(nil), // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 6: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 7: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 8: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 9: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 10: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ - 2, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired - 3, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 4, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 5, // 3: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module - 6, // 4: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 6, // 5: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 7, // 6: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 0, // 7: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest - 8, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 1, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse - 8, // [8:10] is the sub-list for method output_type - 6, // [6:8] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 4, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired + 5, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 6, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 5, // 3: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 6, // 4: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 7, // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 8, // 6: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 8, // 7: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 9, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 2, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest + 10, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 3, // 13: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse + 11, // [11:14] is the sub-list for method output_type + 8, // [8:11] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } @@ -422,13 +565,17 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { (*ReloadResponse_ReloadSuccess)(nil), (*ReloadResponse_ReloadFailed)(nil), } + file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3].OneofWrappers = []any{ + (*WatchResponse_ReloadSuccess)(nil), + (*WatchResponse_ReloadFailed)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index 86170fc8f1..626d1307e6 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -21,6 +21,18 @@ message ReloadResponse { } } + +message WatchRequest { +} + +message WatchResponse { + oneof event { + ReloadSuccess reload_success = 1; + ReloadFailed reload_failed = 2; + } +} + + message ReloadNotRequired {} message ReloadSuccess { @@ -46,4 +58,7 @@ service HotReloadService { // such as when the Reload context changes. // rpc Reload(ReloadRequest) returns (ReloadResponse); + + // Watch for hot reloads not initiated by an explicit Reload call. + rpc Watch(WatchRequest) returns (stream WatchResponse); } diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go index 779cf9709c..879f6c2aef 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go @@ -38,6 +38,8 @@ const ( HotReloadServicePingProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Ping" // HotReloadServiceReloadProcedure is the fully-qualified name of the HotReloadService's Reload RPC. HotReloadServiceReloadProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Reload" + // HotReloadServiceWatchProcedure is the fully-qualified name of the HotReloadService's Watch RPC. + HotReloadServiceWatchProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Watch" ) // HotReloadServiceClient is a client for the xyz.block.ftl.hotreload.v1.HotReloadService service. @@ -47,6 +49,8 @@ type HotReloadServiceClient interface { // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, // such as when the Reload context changes. Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) + // Watch for hot reloads not initiated by an explicit Reload call. + Watch(context.Context, *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) } // NewHotReloadServiceClient constructs a client for the xyz.block.ftl.hotreload.v1.HotReloadService @@ -70,6 +74,11 @@ func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, op baseURL+HotReloadServiceReloadProcedure, opts..., ), + watch: connect.NewClient[v11.WatchRequest, v11.WatchResponse]( + httpClient, + baseURL+HotReloadServiceWatchProcedure, + opts..., + ), } } @@ -77,6 +86,7 @@ func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, op type hotReloadServiceClient struct { ping *connect.Client[v1.PingRequest, v1.PingResponse] reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] + watch *connect.Client[v11.WatchRequest, v11.WatchResponse] } // Ping calls xyz.block.ftl.hotreload.v1.HotReloadService.Ping. @@ -89,6 +99,11 @@ func (c *hotReloadServiceClient) Reload(ctx context.Context, req *connect.Reques return c.reload.CallUnary(ctx, req) } +// Watch calls xyz.block.ftl.hotreload.v1.HotReloadService.Watch. +func (c *hotReloadServiceClient) Watch(ctx context.Context, req *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) { + return c.watch.CallServerStream(ctx, req) +} + // HotReloadServiceHandler is an implementation of the xyz.block.ftl.hotreload.v1.HotReloadService // service. type HotReloadServiceHandler interface { @@ -97,6 +112,8 @@ type HotReloadServiceHandler interface { // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, // such as when the Reload context changes. Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) + // Watch for hot reloads not initiated by an explicit Reload call. + Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error } // NewHotReloadServiceHandler builds an HTTP handler from the service implementation. It returns the @@ -116,12 +133,19 @@ func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.Han svc.Reload, opts..., ) + hotReloadServiceWatchHandler := connect.NewServerStreamHandler( + HotReloadServiceWatchProcedure, + svc.Watch, + opts..., + ) return "/xyz.block.ftl.hotreload.v1.HotReloadService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case HotReloadServicePingProcedure: hotReloadServicePingHandler.ServeHTTP(w, r) case HotReloadServiceReloadProcedure: hotReloadServiceReloadHandler.ServeHTTP(w, r) + case HotReloadServiceWatchProcedure: + hotReloadServiceWatchHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -138,3 +162,7 @@ func (UnimplementedHotReloadServiceHandler) Ping(context.Context, *connect.Reque func (UnimplementedHotReloadServiceHandler) Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Reload is not implemented")) } + +func (UnimplementedHotReloadServiceHandler) Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Watch is not implemented")) +} diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts index 89196e8466..33142f7b26 100644 --- a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts @@ -5,7 +5,7 @@ import { PingRequest, PingResponse } from "../../v1/ftl_pb.js"; import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; -import { ReloadRequest, ReloadResponse } from "./hotreload_pb.js"; +import { ReloadRequest, ReloadResponse, WatchRequest, WatchResponse } from "./hotreload_pb.js"; /** * HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload @@ -40,6 +40,17 @@ export const HotReloadService = { O: ReloadResponse, kind: MethodKind.Unary, }, + /** + * Watch for hot reloads not initiated by an explicit Reload call. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Watch + */ + watch: { + name: "Watch", + I: WatchRequest, + O: WatchResponse, + kind: MethodKind.ServerStreaming, + }, } } as const; diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts index fd25f17905..4414faa340 100644 --- a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts @@ -102,6 +102,87 @@ export class ReloadResponse extends Message { } } +/** + * @generated from message xyz.block.ftl.hotreload.v1.WatchRequest + */ +export class WatchRequest extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.WatchRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): WatchRequest { + return new WatchRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): WatchRequest { + return new WatchRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): WatchRequest { + return new WatchRequest().fromJsonString(jsonString, options); + } + + static equals(a: WatchRequest | PlainMessage | undefined, b: WatchRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(WatchRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.WatchResponse + */ +export class WatchResponse extends Message { + /** + * @generated from oneof xyz.block.ftl.hotreload.v1.WatchResponse.event + */ + event: { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 1; + */ + value: ReloadSuccess; + case: "reloadSuccess"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 2; + */ + value: ReloadFailed; + case: "reloadFailed"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.WatchResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, + { no: 2, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): WatchResponse { + return new WatchResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): WatchResponse { + return new WatchResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): WatchResponse { + return new WatchResponse().fromJsonString(jsonString, options); + } + + static equals(a: WatchResponse | PlainMessage | undefined, b: WatchResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(WatchResponse, a, b); + } +} + /** * @generated from message xyz.block.ftl.hotreload.v1.ReloadNotRequired */ diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index dd8845dbf6..d2e209f25d 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -3,7 +3,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -21,6 +23,8 @@ import xyz.block.ftl.hotreload.v1.ReloadRequest; import xyz.block.ftl.hotreload.v1.ReloadResponse; import xyz.block.ftl.hotreload.v1.ReloadSuccess; +import xyz.block.ftl.hotreload.v1.WatchRequest; +import xyz.block.ftl.hotreload.v1.WatchResponse; import xyz.block.ftl.language.v1.Error; import xyz.block.ftl.language.v1.ErrorList; import xyz.block.ftl.schema.v1.Module; @@ -33,17 +37,44 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private static volatile Module module; - private static volatile ErrorList errors; - private static final AtomicBoolean started = new AtomicBoolean(); - private static volatile Server server; - private static volatile boolean restarting = false; - - synchronized static void setResults(Module module, ErrorList errors) { - HotReloadHandler.module = module; - HotReloadHandler.errors = errors; - restarting = false; - HotReloadHandler.class.notifyAll(); + private static volatile HotReloadHandler INSTANCE; + + private volatile Module module; + private volatile ErrorList errors; + private volatile Server server; + private volatile boolean explicitlyReloading = false; + private final List> watches = Collections.synchronizedList(new ArrayList<>()); + + public static HotReloadHandler getInstance() { + start(); + return INSTANCE; + } + + synchronized void setResults(Module module, ErrorList errors) { + this.module = module; + this.errors = errors; + if (!explicitlyReloading) { + List> watches; + synchronized (this.watches) { + watches = new ArrayList<>(this.watches); + } + for (var watch : watches) { + try { + if (errors == null || errors.getErrorsCount() == 0) { + watch.onNext(WatchResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder().setModule(module).build()).build()); + } else { + watch.onNext(WatchResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder().setErrors(errors).build()).build()); + } + } catch (Exception e) { + LOG.debugf("Failed to send watch response %s", e.toString()); + this.watches.remove(watch); + } + } + } + explicitlyReloading = false; + notifyAll(); } @Override @@ -54,7 +85,6 @@ public void ping(PingRequest request, StreamObserver responseObser @Override public void reload(ReloadRequest request, StreamObserver responseObserver) { - // This is complex, as the restart can't happen until the runner is up // We want to report on the results of the schema generations, so we can bring up a runner // Run the restart in a new thread, so we can report on the schema once it is ready @@ -64,15 +94,15 @@ public void reload(ReloadRequest request, StreamObserver respons doScan(request.getForce()); } finally { synchronized (HotReloadHandler.class) { - restarting = false; + explicitlyReloading = false; HotReloadHandler.class.notifyAll(); } } }, "FTL Restart Thread"); synchronized (HotReloadHandler.class) { - restarting = true; + explicitlyReloading = true; t.start(); - while (restarting) { + while (explicitlyReloading) { try { HotReloadHandler.class.wait(); } catch (InterruptedException e) { @@ -126,23 +156,35 @@ public void reload(ReloadRequest request, StreamObserver respons } } - public static void start() { + @Override + public void watch(WatchRequest request, StreamObserver responseObserver) { + if (module != null || errors != null) { + if (errors == null || errors.getErrorsCount() == 0) { + responseObserver.onNext(WatchResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder().setModule(module).build()).build()); + } else { + responseObserver.onNext(WatchResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder().setErrors(errors).build()).build()); + } + } + watches.add(responseObserver); + } - if (!started.compareAndSet(false, true)) { + public static void start() { + if (INSTANCE != null) { return; } - - for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { - Path migrations = dir.resolve("db"); - if (Files.isDirectory(migrations)) { - try (var stream = Files.walk(migrations)) { - stream.forEach(existingMigrations::add); - } catch (IOException e) { - throw new RuntimeException(e); - } + synchronized (HotReloadHandler.class) { + if (INSTANCE == null) { + var hr = new HotReloadHandler(); + hr.init(); + INSTANCE = hr; } } + } + private void init() { + gatherMigrations(); int port = Integer.getInteger("ftl.language.port"); server = ServerBuilder.forPort(port) .addService(new HotReloadHandler()) @@ -160,10 +202,22 @@ public void run() { server.shutdownNow(); } }); + } + private static void gatherMigrations() { + for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { + Path migrations = dir.resolve("db"); + if (Files.isDirectory(migrations)) { + try (var stream = Files.walk(migrations)) { + stream.forEach(existingMigrations::add); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } } - static void doScan(boolean force) { + void doScan(boolean force) { if (RuntimeUpdatesProcessor.INSTANCE != null) { try { AtomicBoolean newForce = new AtomicBoolean(); diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 69b2f4b53c..27eec24f14 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -189,7 +189,7 @@ public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { - HotReloadHandler.setResults(schRef.get(), errRef.get()); + HotReloadHandler.getInstance().setResults(schRef.get(), errRef.get()); // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. var hash = HashUtil.sha256(schBytes); @@ -206,8 +206,6 @@ public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { recorder.handleDevModeRunnerStart(shutdownContextBuildItem); } } - // TODO: replace runner info file as well - HotReloadHandler.start(); } else { output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); try (var out = Files.newOutputStream(output)) { diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py index c4b9edad86..6f1e1a3430 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\xbf\x01\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x0e\n\x0cWatchRequest\"\xbd\x01\n\rWatchResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\x9f\x02\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponse\x12^\n\x05Watch\x12(.xyz.block.ftl.hotreload.v1.WatchRequest\x1a).xyz.block.ftl.hotreload.v1.WatchResponse0\x01\x42NP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -41,12 +41,16 @@ _globals['_RELOADREQUEST']._serialized_end=219 _globals['_RELOADRESPONSE']._serialized_start=222 _globals['_RELOADRESPONSE']._serialized_end=509 - _globals['_RELOADNOTREQUIRED']._serialized_start=511 - _globals['_RELOADNOTREQUIRED']._serialized_end=530 - _globals['_RELOADSUCCESS']._serialized_start=533 - _globals['_RELOADSUCCESS']._serialized_end=667 - _globals['_RELOADFAILED']._serialized_start=669 - _globals['_RELOADFAILED']._serialized_end=745 - _globals['_HOTRELOADSERVICE']._serialized_start=748 - _globals['_HOTRELOADSERVICE']._serialized_end=939 + _globals['_WATCHREQUEST']._serialized_start=511 + _globals['_WATCHREQUEST']._serialized_end=525 + _globals['_WATCHRESPONSE']._serialized_start=528 + _globals['_WATCHRESPONSE']._serialized_end=717 + _globals['_RELOADNOTREQUIRED']._serialized_start=719 + _globals['_RELOADNOTREQUIRED']._serialized_end=738 + _globals['_RELOADSUCCESS']._serialized_start=741 + _globals['_RELOADSUCCESS']._serialized_end=875 + _globals['_RELOADFAILED']._serialized_start=877 + _globals['_RELOADFAILED']._serialized_end=953 + _globals['_HOTRELOADSERVICE']._serialized_start=956 + _globals['_HOTRELOADSERVICE']._serialized_end=1243 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi index 3a9adc5551..0f389ae504 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi @@ -23,6 +23,18 @@ class ReloadResponse(_message.Message): reload_failed: ReloadFailed def __init__(self, reload_not_required: _Optional[_Union[ReloadNotRequired, _Mapping]] = ..., reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... +class WatchRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class WatchResponse(_message.Message): + __slots__ = ("reload_success", "reload_failed") + RELOAD_SUCCESS_FIELD_NUMBER: _ClassVar[int] + RELOAD_FAILED_FIELD_NUMBER: _ClassVar[int] + reload_success: ReloadSuccess + reload_failed: ReloadFailed + def __init__(self, reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + class ReloadNotRequired(_message.Message): __slots__ = () def __init__(self) -> None: ... From 163248fd1cfddc044b571eb4eb2093e40bbf5a07 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 23 Jan 2025 11:12:35 +1100 Subject: [PATCH 10/11] fix: remove runner info file --- .gitignore | 1 - .../block/ftl/hotreload/v1/hotreload.pb.go | 337 +++++++++++++----- .../block/ftl/hotreload/v1/hotreload.proto | 19 +- .../hotreloadpbconnect/hotreload.connect.go | 35 +- .../xyz/block/ftl/language/v1/language.pb.go | 297 +++++++-------- .../xyz/block/ftl/language/v1/language.proto | 4 +- .../scaling/localscaling/local_scaling.go | 36 +- backend/runner/runner.go | 140 ++++---- .../ftl/hotreload/v1/hotreload_connect.ts | 13 +- .../block/ftl/hotreload/v1/hotreload_pb.ts | 123 +++++++ .../xyz/block/ftl/language/v1/language_pb.ts | 8 +- internal/buildengine/build.go | 6 +- internal/buildengine/languageplugin/plugin.go | 4 +- internal/dev/devendpoint.go | 16 +- .../ftl/deployment/HotReloadHandler.java | 147 +++++--- .../block/ftl/deployment/ModuleProcessor.java | 14 +- .../ftl-runtime/common/hotreload/pom.xml | 14 + .../xyz/block/ftl/hotreload/RunnerInfo.java | 7 + .../ftl/hotreload/RunnerNotification.java | 47 +++ jvm-runtime/ftl-runtime/common/pom.xml | 1 + .../ftl-runtime/common/runtime/pom.xml | 8 +- .../ftl/runtime/DevModeRunnerDetails.java | 61 +--- .../xyz/block/ftl/runtime/FTLController.java | 5 +- .../xyz/block/ftl/runtime/FTLRecorder.java | 11 +- jvm-runtime/ftl-runtime/pom.xml | 5 + jvm-runtime/plugin/common/jvmcommon.go | 56 +-- .../block/ftl/hotreload/v1/hotreload_pb2.py | 24 +- .../block/ftl/hotreload/v1/hotreload_pb2.pyi | 25 +- .../xyz/block/ftl/language/v1/language_pb2.py | 32 +- .../block/ftl/language/v1/language_pb2.pyi | 8 +- 30 files changed, 975 insertions(+), 529 deletions(-) create mode 100644 jvm-runtime/ftl-runtime/common/hotreload/pom.xml create mode 100644 jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java create mode 100644 jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java diff --git a/.gitignore b/.gitignore index 570d41da2a..ad3a92273f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ testdata/**/go.work.sum **/smoketest/**/ftl-module-schema/**/* go-runtime/schema/testdata/test/test.go .cache -.ftl-runner-info # Leaving old _ftl for now to avoid old stuff getting checked in **/testdata/**/_ftl diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go index 1f1dbf8543..fc39c2b4c2 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -283,6 +283,154 @@ func (*WatchResponse_ReloadSuccess) isWatchResponse_Event() {} func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} +type RunnerInfoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Deployment string `protobuf:"bytes,2,opt,name=deployment,proto3" json:"deployment,omitempty"` + Databases []*Database `protobuf:"bytes,3,rep,name=databases,proto3" json:"databases,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RunnerInfoRequest) Reset() { + *x = RunnerInfoRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RunnerInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunnerInfoRequest) ProtoMessage() {} + +func (x *RunnerInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunnerInfoRequest.ProtoReflect.Descriptor instead. +func (*RunnerInfoRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} +} + +func (x *RunnerInfoRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *RunnerInfoRequest) GetDeployment() string { + if x != nil { + return x.Deployment + } + return "" +} + +func (x *RunnerInfoRequest) GetDatabases() []*Database { + if x != nil { + return x.Databases + } + return nil +} + +type Database struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Database) Reset() { + *x = Database{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Database) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Database) ProtoMessage() {} + +func (x *Database) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Database.ProtoReflect.Descriptor instead. +func (*Database) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} +} + +func (x *Database) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Database) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type RunnerInfoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RunnerInfoResponse) Reset() { + *x = RunnerInfoResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RunnerInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunnerInfoResponse) ProtoMessage() {} + +func (x *RunnerInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunnerInfoResponse.ProtoReflect.Descriptor instead. +func (*RunnerInfoResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{6} +} + type ReloadNotRequired struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -291,7 +439,7 @@ type ReloadNotRequired struct { func (x *ReloadNotRequired) Reset() { *x = ReloadNotRequired{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -303,7 +451,7 @@ func (x *ReloadNotRequired) String() string { func (*ReloadNotRequired) ProtoMessage() {} func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -316,7 +464,7 @@ func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadNotRequired.ProtoReflect.Descriptor instead. func (*ReloadNotRequired) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{7} } type ReloadSuccess struct { @@ -331,7 +479,7 @@ type ReloadSuccess struct { func (x *ReloadSuccess) Reset() { *x = ReloadSuccess{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -343,7 +491,7 @@ func (x *ReloadSuccess) String() string { func (*ReloadSuccess) ProtoMessage() {} func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -356,7 +504,7 @@ func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. func (*ReloadSuccess) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{8} } func (x *ReloadSuccess) GetModule() *v1.Module { @@ -383,7 +531,7 @@ type ReloadFailed struct { func (x *ReloadFailed) Reset() { *x = ReloadFailed{} - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -395,7 +543,7 @@ func (x *ReloadFailed) String() string { func (*ReloadFailed) ProtoMessage() {} func (x *ReloadFailed) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -408,7 +556,7 @@ func (x *ReloadFailed) ProtoReflect() protoreflect.Message { // Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. func (*ReloadFailed) Descriptor() ([]byte, []int) { - return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{6} + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{9} } func (x *ReloadFailed) GetErrors() *v11.ErrorList { @@ -465,46 +613,67 @@ var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x13, 0x0a, - 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3c, 0x0a, - 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x4c, 0x0a, 0x0c, 0x52, - 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x9f, 0x02, 0x0a, 0x10, 0x48, 0x6f, - 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, - 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, 0x0a, 0x06, 0x52, 0x65, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x05, 0x57, - 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, - 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x4e, 0x50, 0x01, 0x5a, - 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, - 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x3b, - 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x91, 0x01, + 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, + 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x73, 0x22, 0x38, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x52, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, + 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, + 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x8c, 0x03, + 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, + 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5e, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, + 0x6b, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, + 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, + 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, + 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, + 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -519,40 +688,46 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData } -var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ - (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest - (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse - (*WatchRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.WatchRequest - (*WatchResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.WatchResponse - (*ReloadNotRequired)(nil), // 4: xyz.block.ftl.hotreload.v1.ReloadNotRequired - (*ReloadSuccess)(nil), // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess - (*ReloadFailed)(nil), // 6: xyz.block.ftl.hotreload.v1.ReloadFailed - (*v1.Module)(nil), // 7: xyz.block.ftl.schema.v1.Module - (*v11.ErrorList)(nil), // 8: xyz.block.ftl.language.v1.ErrorList - (*v12.PingRequest)(nil), // 9: xyz.block.ftl.v1.PingRequest - (*v12.PingResponse)(nil), // 10: xyz.block.ftl.v1.PingResponse + (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest + (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse + (*WatchRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.WatchRequest + (*WatchResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.WatchResponse + (*RunnerInfoRequest)(nil), // 4: xyz.block.ftl.hotreload.v1.RunnerInfoRequest + (*Database)(nil), // 5: xyz.block.ftl.hotreload.v1.Database + (*RunnerInfoResponse)(nil), // 6: xyz.block.ftl.hotreload.v1.RunnerInfoResponse + (*ReloadNotRequired)(nil), // 7: xyz.block.ftl.hotreload.v1.ReloadNotRequired + (*ReloadSuccess)(nil), // 8: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 9: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 10: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 11: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 12: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 13: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ - 4, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired - 5, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 6, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 5, // 3: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess - 6, // 4: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed - 7, // 5: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module - 8, // 6: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 8, // 7: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList - 9, // 8: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 0, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest - 2, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest - 10, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 1, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse - 3, // 13: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse - 11, // [11:14] is the sub-list for method output_type - 8, // [8:11] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 7, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired + 8, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 9, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 8, // 3: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 9, // 4: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 5, // 5: xyz.block.ftl.hotreload.v1.RunnerInfoRequest.databases:type_name -> xyz.block.ftl.hotreload.v1.Database + 10, // 6: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 11, // 7: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 11, // 8: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 12, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 2, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest + 4, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo:input_type -> xyz.block.ftl.hotreload.v1.RunnerInfoRequest + 13, // 13: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 14: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 3, // 15: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse + 6, // 16: xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo:output_type -> xyz.block.ftl.hotreload.v1.RunnerInfoResponse + 13, // [13:17] is the sub-list for method output_type + 9, // [9:13] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } @@ -575,7 +750,7 @@ func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 10, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto index 626d1307e6..4e346ea556 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -21,9 +21,7 @@ message ReloadResponse { } } - -message WatchRequest { -} +message WatchRequest {} message WatchResponse { oneof event { @@ -32,6 +30,18 @@ message WatchResponse { } } +message RunnerInfoRequest { + string address = 1; + string deployment = 2; + repeated Database databases = 3; +} + +message Database { + string name = 1; + string address = 2; +} + +message RunnerInfoResponse {} message ReloadNotRequired {} @@ -61,4 +71,7 @@ service HotReloadService { // Watch for hot reloads not initiated by an explicit Reload call. rpc Watch(WatchRequest) returns (stream WatchResponse); + + // Invoked by the runner to provide runner information to the plugin. + rpc RunnerInfo(RunnerInfoRequest) returns (RunnerInfoResponse); } diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go index 879f6c2aef..667b1795ad 100644 --- a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go @@ -40,6 +40,9 @@ const ( HotReloadServiceReloadProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Reload" // HotReloadServiceWatchProcedure is the fully-qualified name of the HotReloadService's Watch RPC. HotReloadServiceWatchProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Watch" + // HotReloadServiceRunnerInfoProcedure is the fully-qualified name of the HotReloadService's + // RunnerInfo RPC. + HotReloadServiceRunnerInfoProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/RunnerInfo" ) // HotReloadServiceClient is a client for the xyz.block.ftl.hotreload.v1.HotReloadService service. @@ -51,6 +54,8 @@ type HotReloadServiceClient interface { Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) // Watch for hot reloads not initiated by an explicit Reload call. Watch(context.Context, *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) + // Invoked by the runner to provide runner information to the plugin. + RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) } // NewHotReloadServiceClient constructs a client for the xyz.block.ftl.hotreload.v1.HotReloadService @@ -79,14 +84,20 @@ func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, op baseURL+HotReloadServiceWatchProcedure, opts..., ), + runnerInfo: connect.NewClient[v11.RunnerInfoRequest, v11.RunnerInfoResponse]( + httpClient, + baseURL+HotReloadServiceRunnerInfoProcedure, + opts..., + ), } } // hotReloadServiceClient implements HotReloadServiceClient. type hotReloadServiceClient struct { - ping *connect.Client[v1.PingRequest, v1.PingResponse] - reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] - watch *connect.Client[v11.WatchRequest, v11.WatchResponse] + ping *connect.Client[v1.PingRequest, v1.PingResponse] + reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] + watch *connect.Client[v11.WatchRequest, v11.WatchResponse] + runnerInfo *connect.Client[v11.RunnerInfoRequest, v11.RunnerInfoResponse] } // Ping calls xyz.block.ftl.hotreload.v1.HotReloadService.Ping. @@ -104,6 +115,11 @@ func (c *hotReloadServiceClient) Watch(ctx context.Context, req *connect.Request return c.watch.CallServerStream(ctx, req) } +// RunnerInfo calls xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo. +func (c *hotReloadServiceClient) RunnerInfo(ctx context.Context, req *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) { + return c.runnerInfo.CallUnary(ctx, req) +} + // HotReloadServiceHandler is an implementation of the xyz.block.ftl.hotreload.v1.HotReloadService // service. type HotReloadServiceHandler interface { @@ -114,6 +130,8 @@ type HotReloadServiceHandler interface { Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) // Watch for hot reloads not initiated by an explicit Reload call. Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error + // Invoked by the runner to provide runner information to the plugin. + RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) } // NewHotReloadServiceHandler builds an HTTP handler from the service implementation. It returns the @@ -138,6 +156,11 @@ func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.Han svc.Watch, opts..., ) + hotReloadServiceRunnerInfoHandler := connect.NewUnaryHandler( + HotReloadServiceRunnerInfoProcedure, + svc.RunnerInfo, + opts..., + ) return "/xyz.block.ftl.hotreload.v1.HotReloadService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case HotReloadServicePingProcedure: @@ -146,6 +169,8 @@ func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.Han hotReloadServiceReloadHandler.ServeHTTP(w, r) case HotReloadServiceWatchProcedure: hotReloadServiceWatchHandler.ServeHTTP(w, r) + case HotReloadServiceRunnerInfoProcedure: + hotReloadServiceRunnerInfoHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -166,3 +191,7 @@ func (UnimplementedHotReloadServiceHandler) Reload(context.Context, *connect.Req func (UnimplementedHotReloadServiceHandler) Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error { return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Watch is not implemented")) } + +func (UnimplementedHotReloadServiceHandler) RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo is not implemented")) +} diff --git a/backend/protos/xyz/block/ftl/language/v1/language.pb.go b/backend/protos/xyz/block/ftl/language/v1/language.pb.go index 745bbf2765..e6ea1d5e23 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.pb.go +++ b/backend/protos/xyz/block/ftl/language/v1/language.pb.go @@ -1271,10 +1271,10 @@ type BuildSuccess struct { DevEndpoint *string `protobuf:"bytes,7,opt,name=dev_endpoint,json=devEndpoint,proto3,oneof" json:"dev_endpoint,omitempty"` // Dev mode debug port DebugPort *int32 `protobuf:"varint,8,opt,name=debug_port,json=debugPort,proto3,oneof" json:"debug_port,omitempty"` - // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin - DevRunnerInfoFile *string `protobuf:"bytes,9,opt,name=dev_runner_info_file,json=devRunnerInfoFile,proto3,oneof" json:"dev_runner_info_file,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process + DevHotReloadEndpoint *string `protobuf:"bytes,9,opt,name=dev_hot_reload_endpoint,json=devHotReloadEndpoint,proto3,oneof" json:"dev_hot_reload_endpoint,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *BuildSuccess) Reset() { @@ -1363,9 +1363,9 @@ func (x *BuildSuccess) GetDebugPort() int32 { return 0 } -func (x *BuildSuccess) GetDevRunnerInfoFile() string { - if x != nil && x.DevRunnerInfoFile != nil { - return *x.DevRunnerInfoFile +func (x *BuildSuccess) GetDevHotReloadEndpoint() string { + if x != nil && x.DevHotReloadEndpoint != nil { + return *x.DevHotReloadEndpoint } return "" } @@ -2047,7 +2047,7 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, - 0x64, 0x22, 0xcc, 0x03, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x64, 0x22, 0xd5, 0x03, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, @@ -2069,154 +2069,155 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x65, 0x76, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, - 0x01, 0x12, 0x34, 0x0a, 0x14, 0x64, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x02, 0x52, 0x11, 0x64, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x46, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x76, 0x5f, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x64, 0x65, 0x76, 0x5f, - 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, - 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, - 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, - 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, - 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, - 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, - 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, - 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, - 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, - 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, - 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, - 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, - 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x19, - 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x01, 0x12, 0x3a, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x02, 0x52, 0x14, 0x64, 0x65, 0x76, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, + 0x0d, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x1a, 0x0a, + 0x18, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, + 0x65, 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, + 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, + 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, - 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x37, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, - 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, - 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, + 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, + 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, + 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, - 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, - 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, + 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, - 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x50, 0x01, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, - 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, + 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, + 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, + 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x50, 0x01, + 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, + 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/backend/protos/xyz/block/ftl/language/v1/language.proto b/backend/protos/xyz/block/ftl/language/v1/language.proto index 62a49bc068..6d4dc932f4 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.proto +++ b/backend/protos/xyz/block/ftl/language/v1/language.proto @@ -237,8 +237,8 @@ message BuildSuccess { // Dev mode debug port optional int32 debug_port = 8; - // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin - optional string dev_runner_info_file = 9; + // Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process + optional string dev_hot_reload_endpoint = 9; } // BuildFailure should be sent when a build fails. diff --git a/backend/provisioner/scaling/localscaling/local_scaling.go b/backend/provisioner/scaling/localscaling/local_scaling.go index e393d417ad..80de96b63e 100644 --- a/backend/provisioner/scaling/localscaling/local_scaling.go +++ b/backend/provisioner/scaling/localscaling/local_scaling.go @@ -100,11 +100,11 @@ func (l *localScaling) TerminatePreviousDeployments(ctx context.Context, module } type devModeRunner struct { - uri url.URL + uri string + hotReloadURI string // The deployment key of the deployment that is currently running - deploymentKey optional.Option[key.Deployment] - debugPort int - runnerInfoFile optional.Option[string] + deploymentKey optional.Option[key.Deployment] + debugPort int } func (l *localScaling) Start(ctx context.Context) error { @@ -122,9 +122,9 @@ func (l *localScaling) Start(ctx context.Context) error { // Must be called under lock func (l *localScaling) updateDevModeEndpoint(ctx context.Context, devEndpoints dev.LocalEndpoint) { l.devModeEndpoints[devEndpoints.Module] = &devModeRunner{ - uri: devEndpoints.Endpoint, - debugPort: devEndpoints.DebugPort, - runnerInfoFile: devEndpoints.RunnerInfoFile, + uri: devEndpoints.Endpoint, + debugPort: devEndpoints.DebugPort, + hotReloadURI: devEndpoints.HotReloadEndpoint, } if ide, ok := l.ideSupport.Get(); ok { if devEndpoints.DebugPort != 0 { @@ -255,12 +255,12 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey key.Deploy } devEndpoint := l.devModeEndpoints[info.module] - devURI := optional.None[url.URL]() - devRunnerInfoFile := optional.None[string]() + devURI := optional.None[string]() + devHotReloadURI := optional.None[string]() debugPort := 0 if devEndpoint != nil { devURI = optional.Some(devEndpoint.uri) - devRunnerInfoFile = devEndpoint.runnerInfoFile + devHotReloadURI = optional.Some(devEndpoint.hotReloadURI) if devKey, ok := devEndpoint.deploymentKey.Get(); ok && devKey.Equal(deploymentKey) { // Already running, don't start another return nil @@ -297,14 +297,14 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey key.Deploy return fmt.Errorf("failed to start runner: %w", err) } config := runner.Config{ - Bind: bindURL, - ControllerEndpoint: controllerEndpoint, - LeaseEndpoint: l.leaseAddress, - Key: key.NewLocalRunnerKey(keySuffix), - Deployment: deploymentKey, - DebugPort: debugPort, - DevEndpoint: devURI, - DevRunnerInfoFile: devRunnerInfoFile, + Bind: bindURL, + ControllerEndpoint: controllerEndpoint, + LeaseEndpoint: l.leaseAddress, + Key: key.NewLocalRunnerKey(keySuffix), + Deployment: deploymentKey, + DebugPort: debugPort, + DevEndpoint: devURI, + DevHotReloadEndpoint: devHotReloadURI, } simpleName := fmt.Sprintf("runner%d", keySuffix) diff --git a/backend/runner/runner.go b/backend/runner/runner.go index aabfb775f9..a974a47b97 100644 --- a/backend/runner/runner.go +++ b/backend/runner/runner.go @@ -29,8 +29,11 @@ import ( "google.golang.org/protobuf/types/known/structpb" mysql "github.com/block/ftl-mysql-auth-proxy" + "github.com/block/ftl/backend/controller/artefacts" ftldeploymentconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/deployment/v1/deploymentpbconnect" + hotreloadpb "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect" ftlleaseconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/lease/v1/leasepbconnect" "github.com/block/ftl/backend/protos/xyz/block/ftl/pubsub/v1/pubsubpbconnect" "github.com/block/ftl/backend/protos/xyz/block/ftl/query/v1/querypbconnect" @@ -57,22 +60,22 @@ import ( ) type Config struct { - Config []string `name:"config" short:"C" help:"Paths to FTL project configuration files." env:"FTL_CONFIG" placeholder:"FILE[,FILE,...]" type:"existingfile"` - Bind *url.URL `help:"Endpoint the Runner should bind to and advertise." default:"http://127.0.0.1:8892" env:"FTL_BIND"` - Key key.Runner `help:"Runner key (auto)."` - ControllerEndpoint *url.URL `name:"ftl-endpoint" help:"Controller endpoint." env:"FTL_ENDPOINT" default:"http://127.0.0.1:8892"` - LeaseEndpoint *url.URL `name:"ftl-lease-endpoint" help:"Lease endpoint endpoint." env:"FTL_LEASE_ENDPOINT" default:"http://127.0.0.1:8895"` - QueryEndpoint *url.URL `name:"ftl-query-endpoint" help:"Query endpoint." env:"FTL_QUERY_ENDPOINT" default:"http://127.0.0.1:8897"` - TimelineEndpoint *url.URL `help:"Timeline endpoint." env:"FTL_TIMELINE_ENDPOINT" default:"http://127.0.0.1:8894"` - TemplateDir string `help:"Template directory to copy into each deployment, if any." type:"existingdir"` - DeploymentDir string `help:"Directory to store deployments in." default:"${deploymentdir}"` - DeploymentKeepHistory int `help:"Number of deployments to keep history for." default:"3"` - HeartbeatPeriod time.Duration `help:"Minimum period between heartbeats." default:"3s"` - HeartbeatJitter time.Duration `help:"Jitter to add to heartbeat period." default:"2s"` - Deployment key.Deployment `help:"The deployment this runner is for." env:"FTL_DEPLOYMENT"` - DebugPort int `help:"The port to use for debugging." env:"FTL_DEBUG_PORT"` - DevEndpoint optional.Option[url.URL] `help:"An existing endpoint to connect to in development mode" hidden:""` - DevRunnerInfoFile optional.Option[string] `help:"The path to a file that we write dev endpoint information to." hidden:""` + Config []string `name:"config" short:"C" help:"Paths to FTL project configuration files." env:"FTL_CONFIG" placeholder:"FILE[,FILE,...]" type:"existingfile"` + Bind *url.URL `help:"Endpoint the Runner should bind to and advertise." default:"http://127.0.0.1:8892" env:"FTL_BIND"` + Key key.Runner `help:"Runner key (auto)."` + ControllerEndpoint *url.URL `name:"ftl-endpoint" help:"Controller endpoint." env:"FTL_ENDPOINT" default:"http://127.0.0.1:8892"` + LeaseEndpoint *url.URL `name:"ftl-lease-endpoint" help:"Lease endpoint endpoint." env:"FTL_LEASE_ENDPOINT" default:"http://127.0.0.1:8895"` + QueryEndpoint *url.URL `name:"ftl-query-endpoint" help:"Query endpoint." env:"FTL_QUERY_ENDPOINT" default:"http://127.0.0.1:8897"` + TimelineEndpoint *url.URL `help:"Timeline endpoint." env:"FTL_TIMELINE_ENDPOINT" default:"http://127.0.0.1:8894"` + TemplateDir string `help:"Template directory to copy into each deployment, if any." type:"existingdir"` + DeploymentDir string `help:"Directory to store deployments in." default:"${deploymentdir}"` + DeploymentKeepHistory int `help:"Number of deployments to keep history for." default:"3"` + HeartbeatPeriod time.Duration `help:"Minimum period between heartbeats." default:"3s"` + HeartbeatJitter time.Duration `help:"Jitter to add to heartbeat period." default:"2s"` + Deployment key.Deployment `help:"The deployment this runner is for." env:"FTL_DEPLOYMENT"` + DebugPort int `help:"The port to use for debugging." env:"FTL_DEBUG_PORT"` + DevEndpoint optional.Option[string] `help:"An existing endpoint to connect to in development mode" hidden:""` + DevHotReloadEndpoint optional.Option[string] `help:"The gRPC enpoint to send runner into to for hot reload." hidden:""` } func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactService) error { @@ -119,17 +122,17 @@ func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactSer timelineClient := timeline.NewClient(ctx, config.TimelineEndpoint) svc := &Service{ - key: runnerKey, - config: config, - storage: storage, - controllerClient: controllerClient, - timelineClient: timelineClient, - labels: labels, - deploymentLogQueue: make(chan log.Entry, 10000), - cancelFunc: doneFunc, - devEndpoint: config.DevEndpoint, - devRunnerInfoFile: config.DevRunnerInfoFile, - queryServices: query.NewMultiService(), + key: runnerKey, + config: config, + storage: storage, + controllerClient: controllerClient, + timelineClient: timelineClient, + labels: labels, + deploymentLogQueue: make(chan log.Entry, 10000), + cancelFunc: doneFunc, + devEndpoint: config.DevEndpoint, + devHotReloadEndpoint: config.DevHotReloadEndpoint, + queryServices: query.NewMultiService(), } module, err := svc.getModule(ctx, config.Deployment) @@ -245,10 +248,11 @@ var _ ftlv1connect.VerbServiceHandler = (*Service)(nil) type deployment struct { key key.Deployment // Cancelled when plugin terminates - ctx context.Context - cmd optional.Option[exec.Cmd] - endpoint *url.URL // The endpoint the plugin is listening on. - client ftlv1connect.VerbServiceClient + ctx context.Context + cmd optional.Option[exec.Cmd] + endpoint string // The endpoint the plugin is listening on. + client ftlv1connect.VerbServiceClient + reverseProxy *httputil.ReverseProxy } type Service struct { @@ -262,16 +266,16 @@ type Service struct { controllerClient ftlv1connect.ControllerServiceClient timelineClient *timeline.Client // Failed to register with the Controller - registrationFailure atomic.Value[optional.Option[error]] - labels *structpb.Struct - deploymentLogQueue chan log.Entry - cancelFunc func() - devEndpoint optional.Option[url.URL] - devRunnerInfoFile optional.Option[string] - proxy *proxy.Service - pubSub *pubsub.Service - queryServices *query.MultiService - proxyBindAddress *url.URL + registrationFailure atomic.Value[optional.Option[error]] + labels *structpb.Struct + deploymentLogQueue chan log.Entry + cancelFunc func() + devEndpoint optional.Option[string] + devHotReloadEndpoint optional.Option[string] + proxy *proxy.Service + pubSub *pubsub.Service + queryServices *query.MultiService + proxyBindAddress *url.URL } func (s *Service) Call(ctx context.Context, req *connect.Request[ftlv1.CallRequest]) (*connect.Response[ftlv1.CallResponse], error) { @@ -387,27 +391,38 @@ func (s *Service) deploy(ctx context.Context, key key.Deployment, module *schema var dep *deployment if ep, ok := s.devEndpoint.Get(); ok { - client := rpc.Dial(ftlv1connect.NewVerbServiceClient, ep.String(), log.Error) - dep = &deployment{ - ctx: ctx, - key: key, - cmd: optional.None[exec.Cmd](), - endpoint: &ep, - client: client, - } - if file, ok := s.devRunnerInfoFile.Get(); ok { - fileContents := "proxy.bind.address=" + s.proxyBindAddress.String() - fileContents += fmt.Sprintf("\ndeployment=%s", s.config.Deployment.String()) + if hotRelaodEp, ok := s.devHotReloadEndpoint.Get(); ok { + hotReloadClient := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, hotRelaodEp, log.Error) + err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, hotReloadClient) + if err != nil { + return fmt.Errorf("failed to ping hot reload endpoint: %w", err) + } + var databases []*hotreloadpb.Database dbAddresses.Range(func(key string, value string) bool { - fileContents += fmt.Sprintf("\ndatabase.%s.url=%s", key, value) + databases = append(databases, &hotreloadpb.Database{ + Name: key, + Address: value, + }) return true }) - err = os.WriteFile(file, []byte(fileContents), 0660) // #nosec + _, err := hotReloadClient.RunnerInfo(ctx, connect.NewRequest(&hotreloadpb.RunnerInfoRequest{ + Deployment: s.config.Deployment.String(), + Address: s.proxyBindAddress.String(), + Databases: databases, + })) if err != nil { - logger.Errorf(err, "could not create FTL dev Config") + return fmt.Errorf("failed to send runner info: %w", err) } } + client := rpc.Dial(ftlv1connect.NewVerbServiceClient, ep, log.Error) err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) + dep = &deployment{ + ctx: ctx, + key: key, + cmd: optional.None[exec.Cmd](), + endpoint: ep, + client: client, + } if err != nil { observability.Deployment.Failure(ctx, optional.Some(key.String())) return fmt.Errorf("failed to ping dev endpoint: %w", err) @@ -510,18 +525,19 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, "no deployment", http.StatusNotFound) return } - proxy := httputil.NewSingleHostReverseProxy(deployment.endpoint) - proxy.ServeHTTP(w, r) + deployment.reverseProxy.ServeHTTP(w, r) } func (s *Service) makeDeployment(ctx context.Context, key key.Deployment, plugin *plugin.Plugin[ftlv1connect.VerbServiceClient, ftlv1.PingRequest, ftlv1.PingResponse, *ftlv1.PingResponse]) *deployment { + proxy := httputil.NewSingleHostReverseProxy(plugin.Endpoint) return &deployment{ - ctx: ctx, - key: key, - cmd: optional.Ptr(plugin.Cmd), - endpoint: plugin.Endpoint, - client: plugin.Client, + ctx: ctx, + key: key, + cmd: optional.Ptr(plugin.Cmd), + endpoint: plugin.Endpoint.String(), + client: plugin.Client, + reverseProxy: proxy, } } diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts index 33142f7b26..06f01b7c0e 100644 --- a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts @@ -5,7 +5,7 @@ import { PingRequest, PingResponse } from "../../v1/ftl_pb.js"; import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; -import { ReloadRequest, ReloadResponse, WatchRequest, WatchResponse } from "./hotreload_pb.js"; +import { ReloadRequest, ReloadResponse, RunnerInfoRequest, RunnerInfoResponse, WatchRequest, WatchResponse } from "./hotreload_pb.js"; /** * HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload @@ -51,6 +51,17 @@ export const HotReloadService = { O: WatchResponse, kind: MethodKind.ServerStreaming, }, + /** + * Invoked by the runner to provide runner information to the plugin. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo + */ + runnerInfo: { + name: "RunnerInfo", + I: RunnerInfoRequest, + O: RunnerInfoResponse, + kind: MethodKind.Unary, + }, } } as const; diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts index 4414faa340..d3444022d4 100644 --- a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts @@ -183,6 +183,129 @@ export class WatchResponse extends Message { } } +/** + * @generated from message xyz.block.ftl.hotreload.v1.RunnerInfoRequest + */ +export class RunnerInfoRequest extends Message { + /** + * @generated from field: string address = 1; + */ + address = ""; + + /** + * @generated from field: string deployment = 2; + */ + deployment = ""; + + /** + * @generated from field: repeated xyz.block.ftl.hotreload.v1.Database databases = 3; + */ + databases: Database[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.RunnerInfoRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "address", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "deployment", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "databases", kind: "message", T: Database, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromJsonString(jsonString, options); + } + + static equals(a: RunnerInfoRequest | PlainMessage | undefined, b: RunnerInfoRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(RunnerInfoRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.Database + */ +export class Database extends Message { + /** + * @generated from field: string name = 1; + */ + name = ""; + + /** + * @generated from field: string address = 2; + */ + address = ""; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.Database"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "address", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Database { + return new Database().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Database { + return new Database().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Database { + return new Database().fromJsonString(jsonString, options); + } + + static equals(a: Database | PlainMessage | undefined, b: Database | PlainMessage | undefined): boolean { + return proto3.util.equals(Database, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.RunnerInfoResponse + */ +export class RunnerInfoResponse extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.RunnerInfoResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromJsonString(jsonString, options); + } + + static equals(a: RunnerInfoResponse | PlainMessage | undefined, b: RunnerInfoResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(RunnerInfoResponse, a, b); + } +} + /** * @generated from message xyz.block.ftl.hotreload.v1.ReloadNotRequired */ diff --git a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts index 66d2df2701..d9ee407631 100644 --- a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts @@ -1170,11 +1170,11 @@ export class BuildSuccess extends Message { debugPort?: number; /** - * Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin + * Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process * - * @generated from field: optional string dev_runner_info_file = 9; + * @generated from field: optional string dev_hot_reload_endpoint = 9; */ - devRunnerInfoFile?: string; + devHotReloadEndpoint?: string; constructor(data?: PartialMessage) { super(); @@ -1192,7 +1192,7 @@ export class BuildSuccess extends Message { { no: 6, name: "errors", kind: "message", T: ErrorList }, { no: 7, name: "dev_endpoint", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 8, name: "debug_port", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - { no: 9, name: "dev_runner_info_file", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 9, name: "dev_hot_reload_endpoint", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): BuildSuccess { diff --git a/internal/buildengine/build.go b/internal/buildengine/build.go index e050a82e26..47ebf44711 100644 --- a/internal/buildengine/build.go +++ b/internal/buildengine/build.go @@ -3,7 +3,6 @@ package buildengine import ( "context" "fmt" - "net/url" "os" "path/filepath" "time" @@ -93,10 +92,7 @@ func handleBuildResult(ctx context.Context, projectConfig projectconfig.Config, } if endpoint, ok := result.DevEndpoint.Get(); ok { if devModeEndpoints != nil { - parsed, err := url.Parse(endpoint) - if err == nil { - devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: *parsed, DebugPort: result.DebugPort, Language: config.Language, RunnerInfoFile: result.DevRunnerInfoFile} - } + devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: endpoint, DebugPort: result.DebugPort, Language: config.Language, HotReloadEndpoint: result.HotReloadEndpoint.Default("")} } } return result.Schema, result.Deploy, nil diff --git a/internal/buildengine/languageplugin/plugin.go b/internal/buildengine/languageplugin/plugin.go index 25801244fe..748ed0a172 100644 --- a/internal/buildengine/languageplugin/plugin.go +++ b/internal/buildengine/languageplugin/plugin.go @@ -41,7 +41,7 @@ type BuildResult struct { DevEndpoint optional.Option[string] // File that the runner can use to pass info into the hot reload endpoint - DevRunnerInfoFile optional.Option[string] + HotReloadEndpoint optional.Option[string] DebugPort int } @@ -581,7 +581,7 @@ func buildResultFromProto(result either.Either[*langpb.BuildResponse_BuildSucces Deploy: buildSuccess.Deploy, StartTime: startTime, DevEndpoint: optional.Ptr(buildSuccess.DevEndpoint), - DevRunnerInfoFile: optional.Ptr(buildSuccess.DevRunnerInfoFile), + HotReloadEndpoint: optional.Ptr(buildSuccess.DevHotReloadEndpoint), DebugPort: port, }, nil case either.Right[*langpb.BuildResponse_BuildSuccess, *langpb.BuildResponse_BuildFailure]: diff --git a/internal/dev/devendpoint.go b/internal/dev/devendpoint.go index ec7f7c2877..6d3906af3b 100644 --- a/internal/dev/devendpoint.go +++ b/internal/dev/devendpoint.go @@ -1,15 +1,9 @@ package dev -import ( - "net/url" - - "github.com/alecthomas/types/optional" -) - type LocalEndpoint struct { - Module string - Endpoint url.URL - DebugPort int - Language string - RunnerInfoFile optional.Option[string] + Module string + Endpoint string + DebugPort int + Language string + HotReloadEndpoint string } diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java index d2e209f25d..3be83f4f72 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -5,7 +5,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -17,12 +19,16 @@ import io.grpc.stub.StreamObserver; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; +import xyz.block.ftl.hotreload.RunnerInfo; +import xyz.block.ftl.hotreload.RunnerNotification; import xyz.block.ftl.hotreload.v1.HotReloadServiceGrpc; import xyz.block.ftl.hotreload.v1.ReloadFailed; import xyz.block.ftl.hotreload.v1.ReloadNotRequired; import xyz.block.ftl.hotreload.v1.ReloadRequest; import xyz.block.ftl.hotreload.v1.ReloadResponse; import xyz.block.ftl.hotreload.v1.ReloadSuccess; +import xyz.block.ftl.hotreload.v1.RunnerInfoRequest; +import xyz.block.ftl.hotreload.v1.RunnerInfoResponse; import xyz.block.ftl.hotreload.v1.WatchRequest; import xyz.block.ftl.hotreload.v1.WatchResponse; import xyz.block.ftl.language.v1.Error; @@ -42,7 +48,8 @@ public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplB private volatile Module module; private volatile ErrorList errors; private volatile Server server; - private volatile boolean explicitlyReloading = false; + private boolean explicitlyReloading = false; + private boolean sentResults = false; private final List> watches = Collections.synchronizedList(new ArrayList<>()); public static HotReloadHandler getInstance() { @@ -73,6 +80,7 @@ synchronized void setResults(Module module, ErrorList errors) { } } } + sentResults = false; explicitlyReloading = false; notifyAll(); } @@ -85,74 +93,85 @@ public void ping(PingRequest request, StreamObserver responseObser @Override public void reload(ReloadRequest request, StreamObserver responseObserver) { + // This is complex, as the restart can't happen until the runner is up // We want to report on the results of the schema generations, so we can bring up a runner // Run the restart in a new thread, so we can report on the schema once it is ready var currentModule = module; - Thread t = new Thread(() -> { - try { - doScan(request.getForce()); - } finally { - synchronized (HotReloadHandler.class) { - explicitlyReloading = false; - HotReloadHandler.class.notifyAll(); - } - } - }, "FTL Restart Thread"); - synchronized (HotReloadHandler.class) { - explicitlyReloading = true; - t.start(); - while (explicitlyReloading) { - try { - HotReloadHandler.class.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); - Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); - if (compileProblem != null || deploymentProblems != null) { - ErrorList.Builder builder = ErrorList.newBuilder(); - if (compileProblem != null) { - builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() - .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(xyz.block.ftl.language.v1.Error.ErrorType.ERROR_TYPE_COMPILER) - .setMsg(compileProblem.getMessage()) + synchronized (HotReloadHandler.this) { + if (explicitlyReloading) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) .build()); + return; } - if (deploymentProblems != null) { - builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() - .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(Error.ErrorType.ERROR_TYPE_FTL) - .setMsg(deploymentProblems.getMessage()) - .build()); + // If we have new results we don't want to re-scan, we want to send them back + if (!sentResults) { + explicitlyReloading = true; + Thread t = new Thread(() -> { + try { + doScan(request.getForce()); + } finally { + synchronized (HotReloadHandler.this) { + explicitlyReloading = false; + HotReloadHandler.this.notifyAll(); + } + } + }, "FTL Restart Thread"); + t.start(); + while (explicitlyReloading) { + try { + HotReloadHandler.this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } - responseObserver.onNext(ReloadResponse.newBuilder() - .setReloadFailed(ReloadFailed.newBuilder() - .setErrors(builder).build()) - .build()); - responseObserver.onCompleted(); - } else if (errors != null && errors.getErrorsCount() > 0) { - responseObserver.onNext(ReloadResponse.newBuilder() - .setReloadFailed(ReloadFailed.newBuilder() - .setErrors(errors).build()) - .build()); - responseObserver.onCompleted(); - } else if (module != null) { - if (module == currentModule) { + sentResults = true; + Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); + Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); + if (compileProblem != null || deploymentProblems != null) { + ErrorList.Builder builder = ErrorList.newBuilder(); + if (compileProblem != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(xyz.block.ftl.language.v1.Error.ErrorType.ERROR_TYPE_COMPILER) + .setMsg(compileProblem.getMessage()) + .build()); + } + if (deploymentProblems != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(Error.ErrorType.ERROR_TYPE_FTL) + .setMsg(deploymentProblems.getMessage()) + .build()); + } responseObserver.onNext(ReloadResponse.newBuilder() - .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(builder).build()) .build()); - } else { + responseObserver.onCompleted(); + } else if (errors != null && errors.getErrorsCount() > 0) { responseObserver.onNext(ReloadResponse.newBuilder() - .setReloadSuccess(ReloadSuccess.newBuilder() - .setModule(module).build()) + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(errors).build()) .build()); + responseObserver.onCompleted(); + } else if (module != null) { + if (module == currentModule) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) + .build()); + } else { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder() + .setModule(module).build()) + .build()); + } + responseObserver.onCompleted(); + } else { + responseObserver.onError(new RuntimeException("schema not generated")); } - responseObserver.onCompleted(); - } else { - responseObserver.onError(new RuntimeException("schema not generated")); } } @@ -170,6 +189,18 @@ public void watch(WatchRequest request, StreamObserver responseOb watches.add(responseObserver); } + @Override + public void runnerInfo(RunnerInfoRequest request, StreamObserver responseObserver) { + Map databases = new HashMap<>(); + for (var db : request.getDatabasesList()) { + databases.put(db.getName(), db.getAddress()); + } + RunnerNotification + .setRunnerInfo(new RunnerInfo(request.getAddress(), request.getDeployment(), databases)); + responseObserver.onNext(RunnerInfoResponse.newBuilder().build()); + responseObserver.onCompleted(); + } + public static void start() { if (INSTANCE != null) { return; @@ -187,7 +218,7 @@ private void init() { gatherMigrations(); int port = Integer.getInteger("ftl.language.port"); server = ServerBuilder.forPort(port) - .addService(new HotReloadHandler()) + .addService(this) .build(); try { LOG.info("Starting Hot Reload gRPC server on port " + port); diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 27eec24f14..3fc8c51620 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -44,6 +44,7 @@ import io.quarkus.runtime.util.HashUtil; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; +import xyz.block.ftl.hotreload.RunnerNotification; import xyz.block.ftl.language.v1.ErrorList; import xyz.block.ftl.runtime.FTLDatasourceCredentials; import xyz.block.ftl.runtime.FTLRecorder; @@ -63,8 +64,6 @@ public class ModuleProcessor { private static final String SCHEMA_OUT = "schema.pb"; private static final String ERRORS_OUT = "errors.pb"; - public static final String DEV_MODE_RUNNER_INFO_FILE = "FTL_RUNNER_INFO"; - /** * Persistent schema hash, used to detect runner restarts in dev mode. */ @@ -195,16 +194,7 @@ public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { var hash = HashUtil.sha256(schBytes); if (!Objects.equals(hash, schemaHash)) { schemaHash = hash; - String runnerInfo = System.getenv(DEV_MODE_RUNNER_INFO_FILE); - if (runnerInfo != null) { - Path path = Path.of(runnerInfo); - // Delete the runner info file if it already exists - Files.deleteIfExists(path); - // This method tells the runtime not to actually start until we have updated runner details - systemPropertyBuildItemBuildProducer - .produce(new SystemPropertyBuildItem(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH, path.toString())); - recorder.handleDevModeRunnerStart(shutdownContextBuildItem); - } + RunnerNotification.setRequiresNewRunnerDetails(); } } else { output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); diff --git a/jvm-runtime/ftl-runtime/common/hotreload/pom.xml b/jvm-runtime/ftl-runtime/common/hotreload/pom.xml new file mode 100644 index 0000000000..bf28b9b7c2 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + + xyz.block.ftl + ftl-jvm-runtime-parent + 1.0-SNAPSHOT + + ftl-jvm-hot-reload + FTL Java Runtime - Hot Reload SPI + + diff --git a/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java new file mode 100644 index 0000000000..dfaa55b63f --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java @@ -0,0 +1,7 @@ +package xyz.block.ftl.hotreload; + +import java.util.Map; + +public record RunnerInfo(String address, String deployment, Map databases) { + +} diff --git a/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java new file mode 100644 index 0000000000..a74492093f --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java @@ -0,0 +1,47 @@ +package xyz.block.ftl.hotreload; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +public class RunnerNotification { + + private static volatile Consumer callback; + private static volatile RunnerInfo info; + private static final AtomicBoolean requiresNewRunnerDetails = new AtomicBoolean(false); + private static final List runnerDetailsCallbacks = Collections.synchronizedList(new ArrayList<>()); + + public static synchronized void setCallback(Consumer callback) { + if (RunnerNotification.callback != null) { + throw new IllegalStateException("Callback already set"); + } + if (info != null) { + callback.accept(info); + info = null; + } else { + RunnerNotification.callback = callback; + } + } + + public static synchronized void setRunnerInfo(RunnerInfo info) { + if (callback != null) { + callback.accept(info); + callback = null; + } else { + RunnerNotification.info = info; + } + } + + public static synchronized void setRequiresNewRunnerDetails() { + for (Runnable callback : runnerDetailsCallbacks) { + callback.run(); + } + } + + public static synchronized void onRunnerDetails(Runnable callback) { + runnerDetailsCallbacks.add(callback); + } + +} diff --git a/jvm-runtime/ftl-runtime/common/pom.xml b/jvm-runtime/ftl-runtime/common/pom.xml index 3aa2e02a32..74a2a85925 100644 --- a/jvm-runtime/ftl-runtime/common/pom.xml +++ b/jvm-runtime/ftl-runtime/common/pom.xml @@ -18,5 +18,6 @@ runtime bom build-parent + hotreload diff --git a/jvm-runtime/ftl-runtime/common/runtime/pom.xml b/jvm-runtime/ftl-runtime/common/runtime/pom.xml index 14f4eb9f14..133ed0e98e 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/pom.xml +++ b/jvm-runtime/ftl-runtime/common/runtime/pom.xml @@ -12,6 +12,10 @@ FTL Java Runtime - Runtime + + xyz.block.ftl + ftl-jvm-hot-reload + io.quarkus quarkus-arc @@ -100,8 +104,8 @@ extension-descriptor - ${project.groupId}:${project.artifactId}-deployment:${project.version} - + ${project.groupId}:${project.artifactId}-deployment:${project.version} + xyz.block.ftl:ftl-jvm-hot-reload diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java index 36fc9235b4..6a31e86510 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java @@ -1,69 +1,32 @@ package xyz.block.ftl.runtime; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; +import xyz.block.ftl.hotreload.RunnerInfo; +import xyz.block.ftl.hotreload.RunnerNotification; public class DevModeRunnerDetails implements RunnerDetails { - private final Path path; private volatile Map databases; private volatile String proxyAddress; private volatile String deployment; private volatile boolean closed; - private static final Pattern dbNames = Pattern.compile("database\\.([a-zA-Z0-9]+).url"); private volatile boolean loaded = false; - public DevModeRunnerDetails(Path path) { - this.path = path; - startWatchThread(); + public DevModeRunnerDetails() { + RunnerNotification.setCallback(this::setRunnerInfo); } - void startWatchThread() { - Thread watchThread = new Thread(() -> { - while (!closed) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - if (Files.exists(path)) { - Properties p = new Properties(); - try (InputStream stream = Files.newInputStream(path)) { - p.load(stream); - synchronized (this) { - proxyAddress = p.getProperty("proxy.bind.address"); - deployment = p.getProperty("deployment"); - var dbs = new HashMap(); - for (var addr : p.stringPropertyNames()) { - Matcher m = dbNames.matcher(addr); - if (m.matches()) { - dbs.put(m.group(1), p.getProperty(addr)); - } - } - databases = dbs; - loaded = true; - notifyAll(); - return; - } - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - }); - watchThread.setDaemon(true); - watchThread.start(); + private void setRunnerInfo(RunnerInfo runnerInfo) { + synchronized (this) { + proxyAddress = runnerInfo.address(); + deployment = runnerInfo.deployment(); + databases = runnerInfo.databases(); + loaded = true; + notifyAll(); + } } @Override diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java index 485272af46..f092166aa8 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java @@ -1,6 +1,5 @@ package xyz.block.ftl.runtime; -import java.nio.file.Path; import java.time.Duration; import java.util.List; import java.util.Map; @@ -13,6 +12,7 @@ import xyz.block.ftl.LeaseFailedException; import xyz.block.ftl.LeaseHandle; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; +import xyz.block.ftl.hotreload.RunnerNotification; public class FTLController implements LeaseClient { private static final Logger log = Logger.getLogger(FTLController.class); @@ -44,6 +44,7 @@ public static FTLController instance() { this.moduleName = System.getProperty("ftl.module.name"); if (LaunchMode.current() != LaunchMode.DEVELOPMENT) { haveRunnerInfo = true; + RunnerNotification.onRunnerDetails(this::devModeShutdown); } } @@ -94,7 +95,7 @@ public void readDevModeRunnerInfo() { runnerConnection = null; } runnerDetails.close(); - runnerDetails = new DevModeRunnerDetails(Path.of(System.getProperty(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH))); + runnerDetails = new DevModeRunnerDetails(); haveRunnerInfo = true; this.notifyAll(); } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java index a4db234ff3..4ed04f1ed8 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.arc.Arc; -import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; import xyz.block.ftl.runtime.http.FTLHttpHandler; @@ -21,7 +20,6 @@ public class FTLRecorder { public static final String X_FTL_VERB = "X-ftl-verb"; - public static final String DEV_MODE_RUNNER_INFO_PATH = "ftl.dev.runner.info"; public void registerVerb(String module, String verbName, String methodName, List> parameterTypes, Class verbHandlerClass, List paramMappers, @@ -159,14 +157,9 @@ public void registerDatabase(String dbKind, GetDeploymentContextResponse.DbType FTLController.instance().registerDatabase(dbKind, name); } - public void handleDevModeRunnerStart(ShutdownContext shutdownContext) { + public void requireNewRunnerDetails() { + FTLController.instance().devModeShutdown(); FTLController.instance().readDevModeRunnerInfo(); - shutdownContext.addShutdownTask(new Runnable() { - @Override - public void run() { - FTLController.instance().devModeShutdown(); - } - }); } public void loadModuleContextOnStartup() { diff --git a/jvm-runtime/ftl-runtime/pom.xml b/jvm-runtime/ftl-runtime/pom.xml index c9e2333377..61426032c4 100644 --- a/jvm-runtime/ftl-runtime/pom.xml +++ b/jvm-runtime/ftl-runtime/pom.xml @@ -73,6 +73,11 @@ pom import + + xyz.block.ftl + ftl-jvm-hot-reload + ${project.version} + xyz.block.ftl ftl-jvm-runtime diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index b8fe5ba202..27666205d5 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -250,6 +250,7 @@ func (s *Service) runDevMode(ctx context.Context, req *connect.Request[langpb.Bu err := s.runQuarkusDev(ctx, req, stream, first) first = false if err != nil { + log.FromContext(ctx).Errorf(err, "Dev mode process exited") return err } if !waitForFileChanges(ctx, fileEvents) { @@ -345,10 +346,8 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } errorFile := filepath.Join(buildCtx.Config.DeployDir, ErrorFile) schemaFile := filepath.Join(buildCtx.Config.DeployDir, SchemaFile) - runnerInfoFile := filepath.Join(buildCtx.Config.Dir, ".ftl-runner-info") os.Remove(errorFile) os.Remove(schemaFile) - os.Remove(runnerInfoFile) errorHash := sha256.SHA256{} schemaHash := sha256.SHA256{} migrationHash := watch.FileHashes{} @@ -369,11 +368,11 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if err == nil { devModeBuild = fmt.Sprintf("%s -Ddebug=%d", devModeBuild, debugPort.Port) } - protoPort, err := plugin.AllocatePort() + hotReloadPort, err := plugin.AllocatePort() if err != nil { return fmt.Errorf("could not allocate port: %w", err) } - devModeBuild = fmt.Sprintf("%s -Dftl.language.port=%d", devModeBuild, protoPort.Port) + devModeBuild = fmt.Sprintf("%s -Dftl.language.port=%d", devModeBuild, hotReloadPort.Port) if os.Getenv("FTL_SUSPEND") == "true" { devModeBuild += " -Dsuspend " @@ -381,8 +380,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb go func() { logger.Infof("Using dev mode build command '%s'", devModeBuild) command := exec.Command(ctx, log.Debug, buildCtx.Config.Dir, "bash", "-c", devModeBuild) - command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind)) - command.Env = append(command.Env, fmt.Sprintf("FTL_RUNNER_INFO=%s", runnerInfoFile)) + command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind), "MAVEN_OPTS=-Xmx1024m") command.Stdout = os.Stdout command.Stderr = os.Stderr err = command.Run() @@ -396,13 +394,14 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb forceUpdate := false // Wait for the plugin to start. - client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, fmt.Sprintf("http://localhost:%d", protoPort.Port), log.Trace) + hotReloadEndpoint := fmt.Sprintf("http://localhost:%d", hotReloadPort.Port) + client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, hotReloadEndpoint, log.Trace) err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) if err != nil { logger.Infof("Dev mode process failed to start") return fmt.Errorf("timed out waiting for start %w", err) } - logger.Infof("Dev mode process started") + logger.Debugf("Dev mode process started") schemaChangeTicker := time.NewTicker(500 * time.Millisecond) defer schemaChangeTicker.Stop() @@ -430,7 +429,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if err != nil { return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } - resp := s.toBuildResponse(result.Msg, &bc.buildCtx, false, devModeEndpoint, debugPort32, runnerInfoFile) + resp := s.toBuildResponse(result.Msg, &bc.buildCtx, false, devModeEndpoint, debugPort32, hotReloadEndpoint) if resp != nil { err = stream.Send(resp) } @@ -440,7 +439,9 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb case <-schemaChangeTicker.C: changed := false if !firstAttempt { + logger.Debugf("Calling reload") _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) + logger.Debugf("Called reload") if err != nil { return fmt.Errorf("failed to invoke hot reload for build context update %w", err) } @@ -461,6 +462,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb schemaHash = sum } } + logger.Debugf("Checking for schema changes") if fileExists(buildCtx.Config.SQLMigrationDirectory) { newMigrationHash, err := watch.ComputeFileHashes(buildCtx.Config.SQLMigrationDirectory, true, []string{"**/*.sql"}) @@ -474,7 +476,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if changed || forceUpdate { auto := !firstAttempt && !forceUpdate if auto { - logger.Infof("sending auto") + logger.Infof("sending auto build event") err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_AutoRebuildStarted{AutoRebuildStarted: &langpb.AutoRebuildStarted{ContextId: buildCtx.ID}}}) if err != nil { return fmt.Errorf("could not send build event: %w", err) @@ -508,17 +510,19 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } - logger.Infof("Live reload schema changed, sending build success event") + if !firstAttempt { + logger.Infof("Live reload schema changed, sending build success event") + } err = stream.Send(&langpb.BuildResponse{ Event: &langpb.BuildResponse_BuildSuccess{ BuildSuccess: &langpb.BuildSuccess{ - ContextId: buildCtx.ID, - IsAutomaticRebuild: auto, - Module: moduleProto, - DevEndpoint: ptr(devModeEndpoint), - DevRunnerInfoFile: &runnerInfoFile, - DebugPort: &debugPort32, - Deploy: []string{SchemaFile}, + ContextId: buildCtx.ID, + IsAutomaticRebuild: auto, + Module: moduleProto, + DevEndpoint: ptr(devModeEndpoint), + DevHotReloadEndpoint: ptr(hotReloadEndpoint), + DebugPort: &debugPort32, + Deploy: []string{SchemaFile}, }, }, }) @@ -970,19 +974,19 @@ func (s *Service) writeGenericSchemaFiles(ctx context.Context, v *schema.Schema, return nil } -func (s *Service) toBuildResponse(result *hotreloadpb.ReloadResponse, bc *buildContext, auto bool, devEndpoint string, debugPort int32, devRunnerInfoFile string) (ret *langpb.BuildResponse) { +func (s *Service) toBuildResponse(result *hotreloadpb.ReloadResponse, bc *buildContext, auto bool, devEndpoint string, debugPort int32, hotReloadAddress string) (ret *langpb.BuildResponse) { ret = &langpb.BuildResponse{} switch e := result.Event.(type) { case *hotreloadpb.ReloadResponse_ReloadSuccess: ret.Event = &langpb.BuildResponse_BuildSuccess{ BuildSuccess: &langpb.BuildSuccess{ - ContextId: bc.ID, - IsAutomaticRebuild: auto, - Module: e.ReloadSuccess.Module, - Errors: e.ReloadSuccess.Errors, - DevEndpoint: &devEndpoint, - DebugPort: &debugPort, - DevRunnerInfoFile: &devRunnerInfoFile, + ContextId: bc.ID, + IsAutomaticRebuild: auto, + Module: e.ReloadSuccess.Module, + Errors: e.ReloadSuccess.Errors, + DevEndpoint: &devEndpoint, + DebugPort: &debugPort, + DevHotReloadEndpoint: &hotReloadAddress, }, } diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py index 6f1e1a3430..5220f45a7d 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x0e\n\x0cWatchRequest\"\xbd\x01\n\rWatchResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\x9f\x02\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponse\x12^\n\x05Watch\x12(.xyz.block.ftl.hotreload.v1.WatchRequest\x1a).xyz.block.ftl.hotreload.v1.WatchResponse0\x01\x42NP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x0e\n\x0cWatchRequest\"\xbd\x01\n\rWatchResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x91\x01\n\x11RunnerInfoRequest\x12\x18\n\x07\x61\x64\x64ress\x18\x01 \x01(\tR\x07\x61\x64\x64ress\x12\x1e\n\ndeployment\x18\x02 \x01(\tR\ndeployment\x12\x42\n\tdatabases\x18\x03 \x03(\x0b\x32$.xyz.block.ftl.hotreload.v1.DatabaseR\tdatabases\"8\n\x08\x44\x61tabase\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07\x61\x64\x64ress\x18\x02 \x01(\tR\x07\x61\x64\x64ress\"\x14\n\x12RunnerInfoResponse\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\x8c\x03\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponse\x12^\n\x05Watch\x12(.xyz.block.ftl.hotreload.v1.WatchRequest\x1a).xyz.block.ftl.hotreload.v1.WatchResponse0\x01\x12k\n\nRunnerInfo\x12-.xyz.block.ftl.hotreload.v1.RunnerInfoRequest\x1a..xyz.block.ftl.hotreload.v1.RunnerInfoResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -45,12 +45,18 @@ _globals['_WATCHREQUEST']._serialized_end=525 _globals['_WATCHRESPONSE']._serialized_start=528 _globals['_WATCHRESPONSE']._serialized_end=717 - _globals['_RELOADNOTREQUIRED']._serialized_start=719 - _globals['_RELOADNOTREQUIRED']._serialized_end=738 - _globals['_RELOADSUCCESS']._serialized_start=741 - _globals['_RELOADSUCCESS']._serialized_end=875 - _globals['_RELOADFAILED']._serialized_start=877 - _globals['_RELOADFAILED']._serialized_end=953 - _globals['_HOTRELOADSERVICE']._serialized_start=956 - _globals['_HOTRELOADSERVICE']._serialized_end=1243 + _globals['_RUNNERINFOREQUEST']._serialized_start=720 + _globals['_RUNNERINFOREQUEST']._serialized_end=865 + _globals['_DATABASE']._serialized_start=867 + _globals['_DATABASE']._serialized_end=923 + _globals['_RUNNERINFORESPONSE']._serialized_start=925 + _globals['_RUNNERINFORESPONSE']._serialized_end=945 + _globals['_RELOADNOTREQUIRED']._serialized_start=947 + _globals['_RELOADNOTREQUIRED']._serialized_end=966 + _globals['_RELOADSUCCESS']._serialized_start=969 + _globals['_RELOADSUCCESS']._serialized_end=1103 + _globals['_RELOADFAILED']._serialized_start=1105 + _globals['_RELOADFAILED']._serialized_end=1181 + _globals['_HOTRELOADSERVICE']._serialized_start=1184 + _globals['_HOTRELOADSERVICE']._serialized_end=1580 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi index 0f389ae504..85a63be648 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi @@ -1,9 +1,10 @@ from xyz.block.ftl.language.v1 import language_pb2 as _language_pb2 from xyz.block.ftl.schema.v1 import schema_pb2 as _schema_pb2 from xyz.block.ftl.v1 import ftl_pb2 as _ftl_pb2 +from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -35,6 +36,28 @@ class WatchResponse(_message.Message): reload_failed: ReloadFailed def __init__(self, reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... +class RunnerInfoRequest(_message.Message): + __slots__ = ("address", "deployment", "databases") + ADDRESS_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + DATABASES_FIELD_NUMBER: _ClassVar[int] + address: str + deployment: str + databases: _containers.RepeatedCompositeFieldContainer[Database] + def __init__(self, address: _Optional[str] = ..., deployment: _Optional[str] = ..., databases: _Optional[_Iterable[_Union[Database, _Mapping]]] = ...) -> None: ... + +class Database(_message.Message): + __slots__ = ("name", "address") + NAME_FIELD_NUMBER: _ClassVar[int] + ADDRESS_FIELD_NUMBER: _ClassVar[int] + name: str + address: str + def __init__(self, name: _Optional[str] = ..., address: _Optional[str] = ...) -> None: ... + +class RunnerInfoResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class ReloadNotRequired(_message.Message): __slots__ = () def __init__(self) -> None: ... diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py index 78895592e7..57c2328990 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\x99\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\x0c \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_build\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xfb\x02\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\t \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lock\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xcc\x03\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x12\x34\n\x14\x64\x65v_runner_info_file\x18\t \x01(\tH\x02R\x11\x64\x65vRunnerInfoFile\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_portB\x17\n\x15_dev_runner_info_file\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xdb\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\x12\x37\n\x06schema\x18\x04 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBLP\x01ZHgithub.com/block/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\x99\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\x0c \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_build\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xfb\x02\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\t \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lock\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xd5\x03\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x12:\n\x17\x64\x65v_hot_reload_endpoint\x18\t \x01(\tH\x02R\x14\x64\x65vHotReloadEndpoint\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_portB\x1a\n\x18_dev_hot_reload_endpoint\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xdb\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\x12\x37\n\x06schema\x18\x04 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBLP\x01ZHgithub.com/block/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -80,19 +80,19 @@ _globals['_AUTOREBUILDSTARTED']._serialized_start=3086 _globals['_AUTOREBUILDSTARTED']._serialized_end=3137 _globals['_BUILDSUCCESS']._serialized_start=3140 - _globals['_BUILDSUCCESS']._serialized_end=3600 - _globals['_BUILDFAILURE']._serialized_start=3603 - _globals['_BUILDFAILURE']._serialized_end=3817 - _globals['_BUILDRESPONSE']._serialized_start=3820 - _globals['_BUILDRESPONSE']._serialized_end=4103 - _globals['_GENERATESTUBSREQUEST']._serialized_start=4106 - _globals['_GENERATESTUBSREQUEST']._serialized_end=4402 - _globals['_GENERATESTUBSRESPONSE']._serialized_start=4404 - _globals['_GENERATESTUBSRESPONSE']._serialized_end=4427 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4430 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4649 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4651 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4679 - _globals['_LANGUAGESERVICE']._serialized_start=4682 - _globals['_LANGUAGESERVICE']._serialized_end=5763 + _globals['_BUILDSUCCESS']._serialized_end=3609 + _globals['_BUILDFAILURE']._serialized_start=3612 + _globals['_BUILDFAILURE']._serialized_end=3826 + _globals['_BUILDRESPONSE']._serialized_start=3829 + _globals['_BUILDRESPONSE']._serialized_end=4112 + _globals['_GENERATESTUBSREQUEST']._serialized_start=4115 + _globals['_GENERATESTUBSREQUEST']._serialized_end=4411 + _globals['_GENERATESTUBSRESPONSE']._serialized_start=4413 + _globals['_GENERATESTUBSRESPONSE']._serialized_end=4436 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4439 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4658 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4660 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4688 + _globals['_LANGUAGESERVICE']._serialized_start=4691 + _globals['_LANGUAGESERVICE']._serialized_end=5772 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi index 107171c3fb..513afae05e 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi @@ -219,7 +219,7 @@ class AutoRebuildStarted(_message.Message): def __init__(self, context_id: _Optional[str] = ...) -> None: ... class BuildSuccess(_message.Message): - __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port", "dev_runner_info_file") + __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port", "dev_hot_reload_endpoint") CONTEXT_ID_FIELD_NUMBER: _ClassVar[int] IS_AUTOMATIC_REBUILD_FIELD_NUMBER: _ClassVar[int] MODULE_FIELD_NUMBER: _ClassVar[int] @@ -228,7 +228,7 @@ class BuildSuccess(_message.Message): ERRORS_FIELD_NUMBER: _ClassVar[int] DEV_ENDPOINT_FIELD_NUMBER: _ClassVar[int] DEBUG_PORT_FIELD_NUMBER: _ClassVar[int] - DEV_RUNNER_INFO_FILE_FIELD_NUMBER: _ClassVar[int] + DEV_HOT_RELOAD_ENDPOINT_FIELD_NUMBER: _ClassVar[int] context_id: str is_automatic_rebuild: bool module: _schema_pb2.Module @@ -237,8 +237,8 @@ class BuildSuccess(_message.Message): errors: ErrorList dev_endpoint: str debug_port: int - dev_runner_info_file: str - def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ..., dev_runner_info_file: _Optional[str] = ...) -> None: ... + dev_hot_reload_endpoint: str + def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ..., dev_hot_reload_endpoint: _Optional[str] = ...) -> None: ... class BuildFailure(_message.Message): __slots__ = ("context_id", "is_automatic_rebuild", "errors", "invalidate_dependencies") From dd388f28f90e391d0e8f97cb16c761d6ef3fbefb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Jan 2025 02:45:39 +0000 Subject: [PATCH 11/11] chore(autofmt): Automated formatting --- backend/runner/runner.go | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/runner/runner.go b/backend/runner/runner.go index a974a47b97..30396ffcf8 100644 --- a/backend/runner/runner.go +++ b/backend/runner/runner.go @@ -29,7 +29,6 @@ import ( "google.golang.org/protobuf/types/known/structpb" mysql "github.com/block/ftl-mysql-auth-proxy" - "github.com/block/ftl/backend/controller/artefacts" ftldeploymentconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/deployment/v1/deploymentpbconnect" hotreloadpb "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1"