diff --git a/cli/azd/.vscode/cspell-azd-dictionary.txt b/cli/azd/.vscode/cspell-azd-dictionary.txt index 47587add004..dc4eafa04b3 100644 --- a/cli/azd/.vscode/cspell-azd-dictionary.txt +++ b/cli/azd/.vscode/cspell-azd-dictionary.txt @@ -110,6 +110,7 @@ envlist envname envsubst errcheck +Errorf errorinfo errorlint eventhub diff --git a/cli/azd/.vscode/cspell.yaml b/cli/azd/.vscode/cspell.yaml index 362aa6790f5..027bd1e8be8 100644 --- a/cli/azd/.vscode/cspell.yaml +++ b/cli/azd/.vscode/cspell.yaml @@ -39,6 +39,7 @@ words: - santhosh - tekuri - jsonschema + - rustc languageSettings: - languageId: go ignoreRegExpList: diff --git a/cli/azd/cmd/container.go b/cli/azd/cmd/container.go index f38450e2cc5..2c1bb73881a 100644 --- a/cli/azd/cmd/container.go +++ b/cli/azd/cmd/container.go @@ -865,6 +865,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) { container.MustRegisterSingleton(grpcserver.NewWorkflowService) container.MustRegisterSingleton(grpcserver.NewExtensionService) container.MustRegisterSingleton(grpcserver.NewServiceTargetService) + container.MustRegisterSingleton(grpcserver.NewFrameworkService) // Required for nested actions called from composite actions like 'up' registerAction[*cmd.ProvisionAction](container, "azd-provision-action") diff --git a/cli/azd/cmd/middleware/extensions.go b/cli/azd/cmd/middleware/extensions.go index 2a636446747..16ac0ab49aa 100644 --- a/cli/azd/cmd/middleware/extensions.go +++ b/cli/azd/cmd/middleware/extensions.go @@ -23,6 +23,7 @@ var ( listenCapabilities = []extensions.CapabilityType{ extensions.LifecycleEventsCapability, extensions.ServiceTargetProviderCapability, + extensions.FrameworkServiceProviderCapability, } ) diff --git a/cli/azd/docs/extension-framework-services.md b/cli/azd/docs/extension-framework-services.md new file mode 100644 index 00000000000..886f6b91f20 --- /dev/null +++ b/cli/azd/docs/extension-framework-services.md @@ -0,0 +1,402 @@ +# Adding Custom Language Frameworks with Extensions + +Azure Developer CLI (azd) extensions can provide custom language framework support beyond the built-in languages (Python, JavaScript, TypeScript, Java, .NET, etc.). This allows you to extend azd to support any programming language or build system that isn't natively supported. + +## Overview + +Framework service extensions enable you to: + +- Add support for new programming languages (Rust, Go, PHP, Ruby, etc.) +- Implement custom build systems and dependency management +- Define language-specific packaging and deployment workflows +- Specify external tool requirements for your language +- Integrate with azd's lifecycle events (restore, build, package) + +## Prerequisites + +- Basic understanding of [azd extensions](./extension-framework.md) +- Go programming knowledge (extensions are written in Go) +- Familiarity with your target language's build tools and ecosystem + +## Creating a Framework Service Extension + +### 1. Extension Structure + +Your extension needs to declare the `framework-service-provider` capability in its `extension.yaml`: + +```yaml +# extension.yaml +id: my.custom.extension +namespace: my.extension +displayName: My Custom Language Extension +description: Adds support for Rust programming language +version: 1.0.0 +capabilities: + - framework-service-provider + - lifecycle-events # Optional: for additional lifecycle hooks +``` + +### 2. Implement the FrameworkServiceProvider Interface + +Create a Go struct that implements the `azdext.FrameworkServiceProvider` interface: + +```go +package main + +import ( + "context" + "fmt" + "time" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" +) + +// Ensure your provider implements the interface +var _ azdext.FrameworkServiceProvider = &RustFrameworkServiceProvider{} + +type RustFrameworkServiceProvider struct { + azdClient *azdext.AzdClient + serviceConfig *azdext.ServiceConfig +} + +func NewRustFrameworkServiceProvider(azdClient *azdext.AzdClient) azdext.FrameworkServiceProvider { + return &RustFrameworkServiceProvider{ + azdClient: azdClient, + } +} +``` + +### 3. Implement Required Methods + +#### Initialize Method + +Called when the framework service is first set up for a service: + +```go +func (p *RustFrameworkServiceProvider) Initialize(ctx context.Context, serviceConfig *azdext.ServiceConfig) error { + fmt.Printf("Initializing Rust framework for service: %s\n", serviceConfig.GetName()) + p.serviceConfig = serviceConfig + + // Perform any initialization logic here + // - Validate Cargo.toml exists + // - Check Rust toolchain version + // - Set up configuration + + return nil +} +``` + +#### RequiredExternalTools Method + +Specify which external tools your language requires: + +```go +func (p *RustFrameworkServiceProvider) RequiredExternalTools( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, +) ([]*azdext.ExternalTool, error) { + return []*azdext.ExternalTool{ + { + Name: "cargo", + InstallUrl: "https://rustup.rs/", + }, + { + Name: "rustc", + InstallUrl: "https://rustup.rs/", + }, + }, nil +} +``` + +#### Requirements Method + +Define what build phases your language needs: + +```go +func (p *RustFrameworkServiceProvider) Requirements() (*azdext.FrameworkRequirements, error) { + return &azdext.FrameworkRequirements{ + Package: &azdext.FrameworkPackageRequirements{ + RequireRestore: true, // Need dependency resolution + RequireBuild: true, // Need compilation step + }, + }, nil +} +``` + +#### Restore Method + +Handle dependency restoration (equivalent to `npm install`, `pip install`, etc.): + +```go +func (p *RustFrameworkServiceProvider) Restore( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + progress azdext.ProgressReporter, +) (*azdext.ServiceRestoreResult, error) { + progress("Installing Rust dependencies") + + // Your actual restore logic here: + // - Run `cargo fetch` to download dependencies + // - Validate Cargo.lock file + // - Check for dependency conflicts + + progress("Cargo dependencies resolved") + + return &azdext.ServiceRestoreResult{ + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "dependencyMgr": "cargo", + "lockFile": "Cargo.lock", + }, + }, nil +} +``` + +#### Build Method + +Handle the compilation/build process: + +```go +func (p *RustFrameworkServiceProvider) Build( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + restoreOutput *azdext.ServiceRestoreResult, + progress azdext.ProgressReporter, +) (*azdext.ServiceBuildResult, error) { + progress("Compiling Rust project") + + // Your actual build logic here: + // - Run `cargo build --release` + // - Handle build errors + // - Generate optimized binary + + progress("Build completed successfully") + + binaryPath := fmt.Sprintf("target/release/%s", serviceConfig.GetName()) + + return &azdext.ServiceBuildResult{ + Restore: restoreOutput, + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "buildMode": "release", + "binaryPath": binaryPath, + "target": "x86_64-unknown-linux-gnu", + }, + }, nil +} +``` + +#### Package Method + +Handle creating deployable artifacts: + +```go +func (p *RustFrameworkServiceProvider) Package( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + buildOutput *azdext.ServiceBuildResult, + progress azdext.ProgressReporter, +) (*azdext.ServicePackageResult, error) { + progress("Creating deployment package") + + // Your actual packaging logic here: + // - Copy binary to deployment directory + // - Include necessary runtime files + // - Create container image or zip archive + // - Generate deployment manifest + + packagePath := fmt.Sprintf("%s-deployment.tar.gz", serviceConfig.GetName()) + progress("Package created: " + packagePath) + + return &azdext.ServicePackageResult{ + PackagePath: packagePath, + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "packageType": "tar.gz", + "size": "15.2MB", + "binary": buildOutput.Details["binaryPath"], + }, + }, nil +} +``` + +### 4. Register the Framework Service + +In your extension's `listen` command, register the framework service provider: + +```go +func newListenCommand() *cobra.Command { + return &cobra.Command{ + Use: "listen", + Short: "Starts the extension and listens for events.", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := azdext.WithAccessToken(cmd.Context()) + + azdClient, err := azdext.NewAzdClient() + if err != nil { + return fmt.Errorf("failed to create azd client: %w", err) + } + defer azdClient.Close() + + // Create your framework service provider + rustFrameworkProvider := NewRustFrameworkServiceProvider(azdClient) + + // Register it with the extension host + host := azdext.NewExtensionHost(azdClient). + WithFrameworkService("rust", rustFrameworkProvider) + + // Start listening for events + return host.Run(ctx) + }, + } +} +``` + +## Using Your Custom Language Framework + +### 1. Project Configuration + +In your `azure.yaml` file, specify your custom language: + +```yaml +# azure.yaml +name: my-rust-app +services: + api: + project: ./api + language: rust # This will use your extension's framework service + host: containerapp +``` + +### 2. Service Configuration + +Your service directory should contain the necessary files for your language: + +``` +api/ +├── Cargo.toml # Rust project manifest +├── Cargo.lock # Dependency lock file +├── src/ +│ └── main.rs # Source code +└── Dockerfile # Optional: custom container image +``` + +### 3. azd Commands + +Once configured, standard azd commands will use your custom framework: + +```bash +# Install dependencies using your Restore method +azd restore + +# Compile using your Build method +azd package + +# Deploy (uses your Package output) +azd deploy +``` + +## Best Practices + +### Error Handling + +Always provide meaningful error messages and handle common failure scenarios: + +```go +func (p *RustFrameworkServiceProvider) Build(ctx context.Context, serviceConfig *azdext.ServiceConfig, restoreOutput *azdext.ServiceRestoreResult, progress azdext.ProgressReporter) (*azdext.ServiceBuildResult, error) { + // Check if Cargo.toml exists + cargoTomlPath := filepath.Join(serviceConfig.GetPath(), "Cargo.toml") + if _, err := os.Stat(cargoTomlPath); os.IsNotExist(err) { + return nil, fmt.Errorf("Cargo.toml not found in %s. This doesn't appear to be a Rust project", serviceConfig.GetPath()) + } + + // Run build command with proper error handling + cmd := exec.CommandContext(ctx, "cargo", "build", "--release") + cmd.Dir = serviceConfig.GetPath() + + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("cargo build failed: %w", err) + } + + // Rest of build logic... +} +``` + +### Progress Reporting + +Use the progress reporter to keep users informed of long-running operations: + +```go +func (p *RustFrameworkServiceProvider) Restore(ctx context.Context, serviceConfig *azdext.ServiceConfig, progress azdext.ProgressReporter) (*azdext.ServiceRestoreResult, error) { + progress("Fetching Rust dependencies...") + // Run cargo fetch + + progress("Resolving dependency tree...") + // Check dependencies + + progress("Dependencies installed successfully") + // Complete +} +``` + +### Configuration Support + +Support common configuration patterns for your language: + +```go +func (p *RustFrameworkServiceProvider) Initialize(ctx context.Context, serviceConfig *azdext.ServiceConfig) error { + // Check for custom build flags in azure.yaml + if buildFlags, exists := serviceConfig.GetEnv()["CARGO_BUILD_FLAGS"]; exists { + p.customBuildFlags = strings.Split(buildFlags, " ") + } + + // Check for target architecture + if target, exists := serviceConfig.GetEnv()["RUST_TARGET"]; exists { + p.targetArch = target + } + + return nil +} +``` + +## Example: Complete Rust Framework Service + +You can find a complete working example in the azd demo extension: + +- Extension manifest: [`extensions/microsoft.azd.demo/extension.yaml`](../extensions/microsoft.azd.demo/extension.yaml) +- Framework service implementation: [`extensions/microsoft.azd.demo/internal/project/framework_service_demo.go`](../extensions/microsoft.azd.demo/internal/project/framework_service_demo.go) +- Registration: [`extensions/microsoft.azd.demo/internal/cmd/listen.go`](../extensions/microsoft.azd.demo/internal/cmd/listen.go) + +## Troubleshooting + +### Common Issues + +1. **Extension not recognized**: Ensure `framework-service-provider` capability is declared in `extension.yaml` + +2. **Language not found**: Check that your framework service is registered with the correct language name in `WithFrameworkService()` + +3. **Build failures**: Verify external tools are installed and available in PATH + +4. **Connection errors**: Make sure your extension implements the `listen` command correctly + +### Debugging + +Enable debug logging to see extension communication: + +```bash +azd config set extension.debug true +azd restore --debug +``` + +## Related Documentation + +- [Extension Framework Overview](./extension-framework.md) +- [Service Target Extensions](./extension-service-targets.md) +- [Extension Development Guide](./new-azd-command.md) + +## Support + +For questions and support: + +- [GitHub Issues](https://github.com/Azure/azure-dev/issues) +- [Azure Developer CLI Documentation](https://docs.microsoft.com/azure/developer/azure-developer-cli/) \ No newline at end of file diff --git a/cli/azd/extensions/extension.schema.json b/cli/azd/extensions/extension.schema.json index e5b4d030331..a834a5198ea 100644 --- a/cli/azd/extensions/extension.schema.json +++ b/cli/azd/extensions/extension.schema.json @@ -136,6 +136,12 @@ "const": "service-target-provider", "title": "Service Target Provider", "description": "Service target provider enables extensions to provide custom service deployment targets." + }, + { + "type": "string", + "const": "framework-service-provider", + "title": "Framework Service Provider", + "description": "Framework service provider enables extensions to provide custom language frameworks and build systems." } ] } diff --git a/cli/azd/extensions/microsoft.azd.demo/extension.yaml b/cli/azd/extensions/microsoft.azd.demo/extension.yaml index fa1a6375615..2730c5058c9 100644 --- a/cli/azd/extensions/microsoft.azd.demo/extension.yaml +++ b/cli/azd/extensions/microsoft.azd.demo/extension.yaml @@ -1,27 +1,28 @@ -# yaml-language-server: $schema=../extension.schema.json -id: microsoft.azd.demo -namespace: demo -displayName: Demo Extension -description: This extension provides examples of the AZD extension framework. -usage: azd demo [options] -version: 0.3.1 -language: go -capabilities: - - custom-commands - - lifecycle-events - - mcp-server - - service-target-provider -providers: - - name: demo - type: service-target - description: Deploys application components to demo -examples: - - name: context - description: Displays the current `azd` project & environment context. - usage: azd demo context - - name: prompt - description: Display prompt capabilities. - usage: azd demo prompt - - name: mcp - description: Start MCP server with demo tools. +# yaml-language-server: $schema=../extension.schema.json +id: microsoft.azd.demo +namespace: demo +displayName: Demo Extension +description: This extension provides examples of the AZD extension framework. +usage: azd demo [options] +version: 0.3.1 +language: go +capabilities: + - custom-commands + - lifecycle-events + - mcp-server + - service-target-provider + - framework-service-provider +providers: + - name: demo + type: service-target + description: Deploys application components to demo +examples: + - name: context + description: Displays the current `azd` project & environment context. + usage: azd demo context + - name: prompt + description: Display prompt capabilities. + usage: azd demo prompt + - name: mcp + description: Start MCP server with demo tools. usage: azd demo mcp start \ No newline at end of file diff --git a/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go b/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go index 618e253d7f7..202e2bd919f 100644 --- a/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go +++ b/cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go @@ -28,9 +28,11 @@ func newListenCommand() *cobra.Command { } defer azdClient.Close() - provider := project.NewDemoServiceTargetProvider(azdClient) + serviceTargetProvider := project.NewDemoServiceTargetProvider(azdClient) + frameworkServiceProvider := project.NewDemoFrameworkServiceProvider(azdClient) host := azdext.NewExtensionHost(azdClient). - WithServiceTarget("demo", provider). + WithServiceTarget("demo", serviceTargetProvider). + WithFrameworkService("rust", frameworkServiceProvider). WithProjectEventHandler("preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error { for i := 1; i <= 20; i++ { fmt.Printf("%d. Doing important work in extension...\n", i) diff --git a/cli/azd/extensions/microsoft.azd.demo/internal/project/framework_service_demo.go b/cli/azd/extensions/microsoft.azd.demo/internal/project/framework_service_demo.go new file mode 100644 index 00000000000..45a9011bd8d --- /dev/null +++ b/cli/azd/extensions/microsoft.azd.demo/internal/project/framework_service_demo.go @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package project + +import ( + "context" + "fmt" + "time" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" +) + +// Ensure DemoFrameworkServiceProvider implements FrameworkServiceProvider interface +var _ azdext.FrameworkServiceProvider = &DemoFrameworkServiceProvider{} + +// DemoFrameworkServiceProvider is a demonstration implementation of a framework service provider +// This shows how to implement support for a custom language/framework that isn't built into azd +type DemoFrameworkServiceProvider struct { + azdClient *azdext.AzdClient + serviceConfig *azdext.ServiceConfig +} + +// NewDemoFrameworkServiceProvider creates a new DemoFrameworkServiceProvider instance +func NewDemoFrameworkServiceProvider(azdClient *azdext.AzdClient) azdext.FrameworkServiceProvider { + return &DemoFrameworkServiceProvider{ + azdClient: azdClient, + } +} + +// Initialize initializes the framework service provider with service configuration +func (p *DemoFrameworkServiceProvider) Initialize(ctx context.Context, serviceConfig *azdext.ServiceConfig) error { + if serviceConfig != nil { + fmt.Printf("Demo framework service initializing for service: %s (language: rust)\n", serviceConfig.GetName()) + } + p.serviceConfig = serviceConfig + return nil +} + +// RequiredExternalTools returns the external tools required by this framework +func (p *DemoFrameworkServiceProvider) RequiredExternalTools( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, +) ([]*azdext.ExternalTool, error) { + // For this demo, we'll say that Rust requires cargo and rustc + tools := []*azdext.ExternalTool{ + { + Name: "cargo", + InstallUrl: "https://rustup.rs/", + }, + { + Name: "rustc", + InstallUrl: "https://rustup.rs/", + }, + } + + return tools, nil +} + +// Requirements returns the framework requirements (whether restore/build are needed) +func (p *DemoFrameworkServiceProvider) Requirements() (*azdext.FrameworkRequirements, error) { + return &azdext.FrameworkRequirements{ + Package: &azdext.FrameworkPackageRequirements{ + RequireRestore: true, // Rust needs cargo build which includes dependency resolution + RequireBuild: true, // Rust needs compilation + }, + }, nil +} + +// Restore performs dependency restoration (like cargo build for dependencies) +func (p *DemoFrameworkServiceProvider) Restore( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + progress azdext.ProgressReporter, +) (*azdext.ServiceRestoreResult, error) { + progress("Installing Rust dependencies") + time.Sleep(500 * time.Millisecond) + + progress("Checking Cargo.toml") + time.Sleep(300 * time.Millisecond) + + progress("Downloading crates") + time.Sleep(800 * time.Millisecond) + + fmt.Printf("\nRust dependencies restored for: %s\n", serviceConfig.GetName()) + + return &azdext.ServiceRestoreResult{ + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "dependencyMgr": "cargo", + "rustVersion": "1.70.0", + }, + }, nil +} + +// Build performs the build operation (cargo build) +func (p *DemoFrameworkServiceProvider) Build( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + restoreOutput *azdext.ServiceRestoreResult, + progress azdext.ProgressReporter, +) (*azdext.ServiceBuildResult, error) { + progress("Compiling Rust project") + time.Sleep(2 * time.Second) + + progress("Linking dependencies") + time.Sleep(2 * time.Second) + + progress("Generating binary") + time.Sleep(3 * time.Second) + + fmt.Printf("\nRust project built: %s\n", serviceConfig.GetName()) + + return &azdext.ServiceBuildResult{ + Restore: restoreOutput, + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "buildMode": "release", + "target": "x86_64-unknown-linux-gnu", + "binaryPath": "target/release/" + serviceConfig.GetName(), + }, + }, nil +} + +// Package performs packaging (creating deployable artifacts) +func (p *DemoFrameworkServiceProvider) Package( + ctx context.Context, + serviceConfig *azdext.ServiceConfig, + buildOutput *azdext.ServiceBuildResult, + progress azdext.ProgressReporter, +) (*azdext.ServicePackageResult, error) { + progress("Creating Rust deployment package") + time.Sleep(600 * time.Millisecond) + + progress("Bundling binary and assets") + time.Sleep(400 * time.Millisecond) + + packagePath := fmt.Sprintf("rust-app-%s.tar.gz", serviceConfig.GetName()) + fmt.Printf("\nRust package created: %s\n", packagePath) + + return &azdext.ServicePackageResult{ + PackagePath: packagePath, + Details: map[string]string{ + "timestamp": time.Now().Format(time.RFC3339), + "packageType": "tar.gz", + "binaryIncluded": "true", + "size": "15.2MB", + }, + }, nil +} diff --git a/cli/azd/grpc/proto/extension.proto b/cli/azd/grpc/proto/extension.proto index ffb9e3e38d3..3542e661c18 100644 --- a/cli/azd/grpc/proto/extension.proto +++ b/cli/azd/grpc/proto/extension.proto @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. syntax = "proto3"; package azdext; diff --git a/cli/azd/grpc/proto/framework_service.proto b/cli/azd/grpc/proto/framework_service.proto new file mode 100644 index 00000000000..145256ed909 --- /dev/null +++ b/cli/azd/grpc/proto/framework_service.proto @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +syntax = "proto3"; + +package azdext; + +option go_package = "github.com/azure/azure-dev/cli/azd/pkg/azdext"; + +import "models.proto"; +import "service_target.proto"; + +service FrameworkService { + // Bidirectional stream for framework service requests and responses + rpc Stream(stream FrameworkServiceMessage) returns (stream FrameworkServiceMessage); +} + +// Envelope for all possible framework service messages (requests and responses) +message FrameworkServiceMessage { + string request_id = 1; + FrameworkServiceErrorMessage error = 99; + oneof message_type { + RegisterFrameworkServiceRequest register_framework_service_request = 2; + RegisterFrameworkServiceResponse register_framework_service_response = 3; + FrameworkServiceInitializeRequest initialize_request = 4; + FrameworkServiceInitializeResponse initialize_response = 5; + FrameworkServiceRequiredExternalToolsRequest required_external_tools_request = 6; + FrameworkServiceRequiredExternalToolsResponse required_external_tools_response = 7; + FrameworkServiceRequirementsRequest requirements_request = 8; + FrameworkServiceRequirementsResponse requirements_response = 9; + FrameworkServiceRestoreRequest restore_request = 10; + FrameworkServiceRestoreResponse restore_response = 11; + FrameworkServiceBuildRequest build_request = 12; + FrameworkServiceBuildResponse build_response = 13; + FrameworkServicePackageRequest package_request = 14; + FrameworkServicePackageResponse package_response = 15; + FrameworkServiceProgressMessage progress_message = 16; + } +} + +// Error message for framework service operations +message FrameworkServiceErrorMessage { + string message = 1; + string details = 2; +} + +// Request to register a framework service provider +message RegisterFrameworkServiceRequest { + string language = 1; // unique identifier for the language/framework (e.g., "rust", "go", "php") +} + +message RegisterFrameworkServiceResponse { + // Empty for now +} + +// Initialize request and response +message FrameworkServiceInitializeRequest { + ServiceConfig service_config = 1; +} + +message FrameworkServiceInitializeResponse { + // Empty for now +} + +// Required external tools request and response +message FrameworkServiceRequiredExternalToolsRequest { + ServiceConfig service_config = 1; +} + +message FrameworkServiceRequiredExternalToolsResponse { + repeated ExternalTool tools = 1; +} + +// External tool definition +message ExternalTool { + string name = 1; + string install_url = 2; +} + +// Requirements request and response +message FrameworkServiceRequirementsRequest { + // Empty - requirements are static for a framework +} + +message FrameworkServiceRequirementsResponse { + FrameworkRequirements requirements = 1; +} + +// Framework requirements definition +message FrameworkRequirements { + FrameworkPackageRequirements package = 1; +} + +message FrameworkPackageRequirements { + bool require_restore = 1; + bool require_build = 2; +} + +// Restore request and response +message FrameworkServiceRestoreRequest { + ServiceConfig service_config = 1; +} + +message FrameworkServiceRestoreResponse { + ServiceRestoreResult restore_result = 1; +} + +// Service restore result +message ServiceRestoreResult { + map details = 1; +} + +// Build request and response +message FrameworkServiceBuildRequest { + ServiceConfig service_config = 1; + ServiceRestoreResult restore_output = 2; +} + +message FrameworkServiceBuildResponse { + ServiceBuildResult build_result = 1; +} + +// Service build result +message ServiceBuildResult { + ServiceRestoreResult restore = 1; + map details = 2; +} + +// Package request and response +message FrameworkServicePackageRequest { + ServiceConfig service_config = 1; + ServiceBuildResult build_output = 2; +} + +message FrameworkServicePackageResponse { + ServicePackageResult package_result = 1; +} + +// ServicePackageResult is imported from service_target.proto + +// Progress message for framework service operations +message FrameworkServiceProgressMessage { + string request_id = 1; + string message = 2; + int64 timestamp = 3; // Unix timestamp in milliseconds +} \ No newline at end of file diff --git a/cli/azd/grpc/proto/service_target.proto b/cli/azd/grpc/proto/service_target.proto index 9f75716eae4..94ad3f23393 100644 --- a/cli/azd/grpc/proto/service_target.proto +++ b/cli/azd/grpc/proto/service_target.proto @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. syntax = "proto3"; package azdext; diff --git a/cli/azd/internal/cmd/show/show.go b/cli/azd/internal/cmd/show/show.go index efc52b0aad6..fef45da48db 100644 --- a/cli/azd/internal/cmd/show/show.go +++ b/cli/azd/internal/cmd/show/show.go @@ -540,7 +540,9 @@ func showTypeFromLanguage(language project.ServiceLanguageKind) contracts.ShowTy case project.ServiceLanguageJava: return contracts.ShowTypeJava default: - panic(fmt.Sprintf("unknown language %s", language)) + // Handle extension languages gracefully by returning a generic type + // Extension framework services will handle the actual operations + return contracts.ShowTypeNone } } diff --git a/cli/azd/internal/grpcserver/framework_service.go b/cli/azd/internal/grpcserver/framework_service.go new file mode 100644 index 00000000000..1be0d1ab9ed --- /dev/null +++ b/cli/azd/internal/grpcserver/framework_service.go @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package grpcserver + +import ( + "errors" + "fmt" + "io" + "log" + + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/extensions" + "github.com/azure/azure-dev/cli/azd/pkg/input" + "github.com/azure/azure-dev/cli/azd/pkg/ioc" + "github.com/azure/azure-dev/cli/azd/pkg/project" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// FrameworkService implements azdext.FrameworkServiceServer. +type FrameworkService struct { + azdext.UnimplementedFrameworkServiceServer + container *ioc.NestedContainer + extensionManager *extensions.Manager + providerMap map[string]azdext.FrameworkService_StreamServer +} + +// NewFrameworkService creates a new FrameworkService instance. +func NewFrameworkService( + container *ioc.NestedContainer, + extensionManager *extensions.Manager, +) azdext.FrameworkServiceServer { + return &FrameworkService{ + container: container, + extensionManager: extensionManager, + providerMap: make(map[string]azdext.FrameworkService_StreamServer), + } +} + +// Stream handles the bi-directional streaming for framework service operations. +func (s *FrameworkService) Stream( + stream azdext.FrameworkService_StreamServer, +) error { + ctx := stream.Context() + extensionClaims, err := GetExtensionClaims(ctx) + if err != nil { + return fmt.Errorf("failed to get extension claims: %w", err) + } + + options := extensions.FilterOptions{ + Id: extensionClaims.Subject, + } + + extension, err := s.extensionManager.GetInstalled(options) + if err != nil { + return status.Errorf(codes.FailedPrecondition, "failed to get extension: %s", err.Error()) + } + + // For framework services, we'll create a custom capability check similar to service targets + // Extensions providing framework services should declare this capability + if !extension.HasCapability("framework-service-provider") { + return status.Errorf(codes.PermissionDenied, "extension does not support framework-service-provider capability") + } + + msg, err := stream.Recv() + if errors.Is(err, io.EOF) { + return nil + } + if err != nil { + return err + } + + regRequest := msg.GetRegisterFrameworkServiceRequest() + if regRequest == nil { + return status.Errorf( + codes.FailedPrecondition, + "expected RegisterFrameworkServiceRequest, got %T", + msg.GetMessageType(), + ) + } + + language := regRequest.GetLanguage() + if _, has := s.providerMap[language]; has { + return status.Errorf(codes.AlreadyExists, "provider %s already registered", language) + } + + // Register external framework service with DI container + err = s.container.RegisterNamedSingleton(language, func( + console input.Console, + ) project.FrameworkService { + return project.NewExternalFrameworkService( + language, + project.ServiceLanguageKind(language), + extension, + stream, + console, + ) + }) + + if err != nil { + return status.Errorf(codes.Internal, "failed to register framework service: %s", err.Error()) + } + + s.providerMap[language] = stream + + // Send registration response + response := &azdext.FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &azdext.FrameworkServiceMessage_RegisterFrameworkServiceResponse{ + RegisterFrameworkServiceResponse: &azdext.RegisterFrameworkServiceResponse{}, + }, + } + + if err := stream.Send(response); err != nil { + return err + } + + // The stream is now handled by the ExternalFrameworkService, so we don't consume messages here. + // We need to wait for the stream to close without consuming messages. + select { + case <-ctx.Done(): + log.Printf("Framework service stream for language '%s' cancelled", language) + case <-stream.Context().Done(): + log.Printf("Framework service stream for language '%s' closed by client", language) + } + + // Clean up when stream closes + delete(s.providerMap, language) + + return nil +} diff --git a/cli/azd/internal/grpcserver/server.go b/cli/azd/internal/grpcserver/server.go index 147c995a9cf..c5b3e4e2dd3 100644 --- a/cli/azd/internal/grpcserver/server.go +++ b/cli/azd/internal/grpcserver/server.go @@ -35,6 +35,7 @@ type Server struct { workflowService azdext.WorkflowServiceServer extensionService azdext.ExtensionServiceServer serviceTargetService azdext.ServiceTargetServiceServer + frameworkService azdext.FrameworkServiceServer } func NewServer( @@ -48,6 +49,7 @@ func NewServer( workflowService azdext.WorkflowServiceServer, extensionService azdext.ExtensionServiceServer, serviceTargetService azdext.ServiceTargetServiceServer, + frameworkService azdext.FrameworkServiceServer, ) *Server { return &Server{ projectService: projectService, @@ -60,6 +62,7 @@ func NewServer( workflowService: workflowService, extensionService: extensionService, serviceTargetService: serviceTargetService, + frameworkService: frameworkService, } } @@ -95,6 +98,7 @@ func (s *Server) Start() (*ServerInfo, error) { azdext.RegisterWorkflowServiceServer(s.grpcServer, s.workflowService) azdext.RegisterExtensionServiceServer(s.grpcServer, s.extensionService) azdext.RegisterServiceTargetServiceServer(s.grpcServer, s.serviceTargetService) + azdext.RegisterFrameworkServiceServer(s.grpcServer, s.frameworkService) serverInfo.Address = fmt.Sprintf("localhost:%d", randomPort) serverInfo.Port = randomPort diff --git a/cli/azd/internal/grpcserver/server_test.go b/cli/azd/internal/grpcserver/server_test.go index d1a076b34b0..33f24503bab 100644 --- a/cli/azd/internal/grpcserver/server_test.go +++ b/cli/azd/internal/grpcserver/server_test.go @@ -32,6 +32,7 @@ func Test_Server_Start(t *testing.T) { azdext.UnimplementedWorkflowServiceServer{}, azdext.UnimplementedExtensionServiceServer{}, azdext.UnimplementedServiceTargetServiceServer{}, + azdext.UnimplementedFrameworkServiceServer{}, ) serverInfo, err := server.Start() diff --git a/cli/azd/pkg/azdext/azd_client.go b/cli/azd/pkg/azdext/azd_client.go index 397fbdbf23f..baec42926a4 100644 --- a/cli/azd/pkg/azdext/azd_client.go +++ b/cli/azd/pkg/azdext/azd_client.go @@ -156,6 +156,12 @@ func (c *AzdClient) ServiceTarget() ServiceTargetServiceClient { return c.serviceTargetClient } +// FrameworkService returns the framework service client. +func (c *AzdClient) FrameworkService() FrameworkServiceClient { + // Create framework service client directly as it's not yet added to the client struct + return NewFrameworkServiceClient(c.connection) +} + func (c *AzdClient) extensionService() ExtensionServiceClient { if c.extensionClient == nil { c.extensionClient = NewExtensionServiceClient(c.connection) diff --git a/cli/azd/pkg/azdext/compose.pb.go b/cli/azd/pkg/azdext/compose.pb.go index 1c586efbb9d..b5bc60ae3b5 100644 --- a/cli/azd/pkg/azdext/compose.pb.go +++ b/cli/azd/pkg/azdext/compose.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: compose.proto package azdext diff --git a/cli/azd/pkg/azdext/compose_grpc.pb.go b/cli/azd/pkg/azdext/compose_grpc.pb.go index 93e3eb26943..bc2833c01ba 100644 --- a/cli/azd/pkg/azdext/compose_grpc.pb.go +++ b/cli/azd/pkg/azdext/compose_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: compose.proto package azdext diff --git a/cli/azd/pkg/azdext/deployment.pb.go b/cli/azd/pkg/azdext/deployment.pb.go index d16d63fad4e..5ce0f2b14c8 100644 --- a/cli/azd/pkg/azdext/deployment.pb.go +++ b/cli/azd/pkg/azdext/deployment.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: deployment.proto package azdext diff --git a/cli/azd/pkg/azdext/deployment_grpc.pb.go b/cli/azd/pkg/azdext/deployment_grpc.pb.go index 38f338d0a7f..f345d9b491e 100644 --- a/cli/azd/pkg/azdext/deployment_grpc.pb.go +++ b/cli/azd/pkg/azdext/deployment_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: deployment.proto package azdext diff --git a/cli/azd/pkg/azdext/environment.pb.go b/cli/azd/pkg/azdext/environment.pb.go index 1868c1a5f75..465da667245 100644 --- a/cli/azd/pkg/azdext/environment.pb.go +++ b/cli/azd/pkg/azdext/environment.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: environment.proto package azdext diff --git a/cli/azd/pkg/azdext/environment_grpc.pb.go b/cli/azd/pkg/azdext/environment_grpc.pb.go index b8c216d8ac7..0795f439e16 100644 --- a/cli/azd/pkg/azdext/environment_grpc.pb.go +++ b/cli/azd/pkg/azdext/environment_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: environment.proto package azdext diff --git a/cli/azd/pkg/azdext/event.pb.go b/cli/azd/pkg/azdext/event.pb.go index 841688d9df8..379c42ad01a 100644 --- a/cli/azd/pkg/azdext/event.pb.go +++ b/cli/azd/pkg/azdext/event.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: event.proto package azdext diff --git a/cli/azd/pkg/azdext/event_grpc.pb.go b/cli/azd/pkg/azdext/event_grpc.pb.go index b28f9ba378d..90c72a5ce44 100644 --- a/cli/azd/pkg/azdext/event_grpc.pb.go +++ b/cli/azd/pkg/azdext/event_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: event.proto package azdext diff --git a/cli/azd/pkg/azdext/extension.pb.go b/cli/azd/pkg/azdext/extension.pb.go index d37de7d081e..69494a2cff9 100644 --- a/cli/azd/pkg/azdext/extension.pb.go +++ b/cli/azd/pkg/azdext/extension.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: extension.proto package azdext diff --git a/cli/azd/pkg/azdext/extension_grpc.pb.go b/cli/azd/pkg/azdext/extension_grpc.pb.go index da1624c9272..f238e4448e1 100644 --- a/cli/azd/pkg/azdext/extension_grpc.pb.go +++ b/cli/azd/pkg/azdext/extension_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: extension.proto package azdext diff --git a/cli/azd/pkg/azdext/extension_host.go b/cli/azd/pkg/azdext/extension_host.go index c9c7d6dcc9c..a7a2b4d2fd6 100644 --- a/cli/azd/pkg/azdext/extension_host.go +++ b/cli/azd/pkg/azdext/extension_host.go @@ -17,6 +17,11 @@ type serviceTargetRegistrar interface { Close() error } +type frameworkServiceRegistrar interface { + Register(ctx context.Context, provider FrameworkServiceProvider, language string) error + Close() error +} + type extensionEventManager interface { AddProjectEventHandler(ctx context.Context, eventName string, handler ProjectEventHandler) error AddServiceEventHandler( @@ -32,6 +37,12 @@ type ServiceTargetRegistration struct { Provider ServiceTargetProvider } +// FrameworkServiceRegistration describes a framework service provider to register with azd core. +type FrameworkServiceRegistration struct { + Language string + Provider FrameworkServiceProvider +} + // ProjectEventRegistration describes a project-level event handler to register. type ProjectEventRegistration struct { EventName string @@ -49,13 +60,15 @@ type ServiceEventRegistration struct { type ExtensionHost struct { client *AzdClient - serviceTargets []ServiceTargetRegistration - projectHandlers []ProjectEventRegistration - serviceHandlers []ServiceEventRegistration + serviceTargets []ServiceTargetRegistration + frameworkServices []FrameworkServiceRegistration + projectHandlers []ProjectEventRegistration + serviceHandlers []ServiceEventRegistration - newServiceTargetManager func(*AzdClient) serviceTargetRegistrar - newEventManager func(*AzdClient) extensionEventManager - readyFn func(context.Context) error + newServiceTargetManager func(*AzdClient) serviceTargetRegistrar + newFrameworkServiceManager func(*AzdClient) frameworkServiceRegistrar + newEventManager func(*AzdClient) extensionEventManager + readyFn func(context.Context) error } // NewExtensionHost creates a new ExtensionHost for the supplied azd client. @@ -65,6 +78,9 @@ func NewExtensionHost(client *AzdClient) *ExtensionHost { newServiceTargetManager: func(c *AzdClient) serviceTargetRegistrar { return NewServiceTargetManager(c) }, + newFrameworkServiceManager: func(c *AzdClient) frameworkServiceRegistrar { + return NewFrameworkServiceManager(c) + }, newEventManager: func(c *AzdClient) extensionEventManager { return NewEventManager(c) }, @@ -80,6 +96,12 @@ func (er *ExtensionHost) WithServiceTarget(host string, provider ServiceTargetPr return er } +// WithFrameworkService registers a framework service provider to be wired when Run is invoked. +func (er *ExtensionHost) WithFrameworkService(language string, provider FrameworkServiceProvider) *ExtensionHost { + er.frameworkServices = append(er.frameworkServices, FrameworkServiceRegistration{Language: language, Provider: provider}) + return er +} + // WithProjectEventHandler registers a project-level event handler to be wired when Run is invoked. func (er *ExtensionHost) WithProjectEventHandler(eventName string, handler ProjectEventHandler) *ExtensionHost { er.projectHandlers = append(er.projectHandlers, ProjectEventRegistration{EventName: eventName, Handler: handler}) @@ -103,6 +125,7 @@ func (er *ExtensionHost) WithServiceEventHandler( // Run wires the configured service targets and event handlers, signals readiness, and blocks until shutdown. func (er *ExtensionHost) Run(ctx context.Context) error { var serviceManagers []serviceTargetRegistrar + var frameworkManagers []frameworkServiceRegistrar for _, reg := range er.serviceTargets { if reg.Provider == nil { @@ -123,6 +146,28 @@ func (er *ExtensionHost) Run(ctx context.Context) error { serviceManagers = append(serviceManagers, manager) } + for _, reg := range er.frameworkServices { + if reg.Provider == nil { + return fmt.Errorf("framework service provider for language '%s' is nil", reg.Language) + } + + manager := er.newFrameworkServiceManager(er.client) + if err := manager.Register(ctx, reg.Provider, reg.Language); err != nil { + _ = manager.Close() + + for _, registered := range frameworkManagers { + _ = registered.Close() + } + for _, registered := range serviceManagers { + _ = registered.Close() + } + + return fmt.Errorf("failed to register framework service '%s': %w", reg.Language, err) + } + + frameworkManagers = append(frameworkManagers, manager) + } + if len(serviceManagers) > 0 { defer func() { for i := len(serviceManagers) - 1; i >= 0; i-- { @@ -131,6 +176,14 @@ func (er *ExtensionHost) Run(ctx context.Context) error { }() } + if len(frameworkManagers) > 0 { + defer func() { + for i := len(frameworkManagers) - 1; i >= 0; i-- { + _ = frameworkManagers[i].Close() + } + }() + } + if len(er.projectHandlers) == 0 && len(er.serviceHandlers) == 0 { return er.readyFn(ctx) } diff --git a/cli/azd/pkg/azdext/framework_service.pb.go b/cli/azd/pkg/azdext/framework_service.pb.go new file mode 100644 index 00000000000..ee8c8a3c1d8 --- /dev/null +++ b/cli/azd/pkg/azdext/framework_service.pb.go @@ -0,0 +1,1523 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v6.32.1 +// source: framework_service.proto + +package azdext + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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) +) + +// Envelope for all possible framework service messages (requests and responses) +type FrameworkServiceMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Error *FrameworkServiceErrorMessage `protobuf:"bytes,99,opt,name=error,proto3" json:"error,omitempty"` + // Types that are valid to be assigned to MessageType: + // + // *FrameworkServiceMessage_RegisterFrameworkServiceRequest + // *FrameworkServiceMessage_RegisterFrameworkServiceResponse + // *FrameworkServiceMessage_InitializeRequest + // *FrameworkServiceMessage_InitializeResponse + // *FrameworkServiceMessage_RequiredExternalToolsRequest + // *FrameworkServiceMessage_RequiredExternalToolsResponse + // *FrameworkServiceMessage_RequirementsRequest + // *FrameworkServiceMessage_RequirementsResponse + // *FrameworkServiceMessage_RestoreRequest + // *FrameworkServiceMessage_RestoreResponse + // *FrameworkServiceMessage_BuildRequest + // *FrameworkServiceMessage_BuildResponse + // *FrameworkServiceMessage_PackageRequest + // *FrameworkServiceMessage_PackageResponse + // *FrameworkServiceMessage_ProgressMessage + MessageType isFrameworkServiceMessage_MessageType `protobuf_oneof:"message_type"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceMessage) Reset() { + *x = FrameworkServiceMessage{} + mi := &file_framework_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceMessage) ProtoMessage() {} + +func (x *FrameworkServiceMessage) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 FrameworkServiceMessage.ProtoReflect.Descriptor instead. +func (*FrameworkServiceMessage) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{0} +} + +func (x *FrameworkServiceMessage) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *FrameworkServiceMessage) GetError() *FrameworkServiceErrorMessage { + if x != nil { + return x.Error + } + return nil +} + +func (x *FrameworkServiceMessage) GetMessageType() isFrameworkServiceMessage_MessageType { + if x != nil { + return x.MessageType + } + return nil +} + +func (x *FrameworkServiceMessage) GetRegisterFrameworkServiceRequest() *RegisterFrameworkServiceRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RegisterFrameworkServiceRequest); ok { + return x.RegisterFrameworkServiceRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRegisterFrameworkServiceResponse() *RegisterFrameworkServiceResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RegisterFrameworkServiceResponse); ok { + return x.RegisterFrameworkServiceResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetInitializeRequest() *FrameworkServiceInitializeRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_InitializeRequest); ok { + return x.InitializeRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetInitializeResponse() *FrameworkServiceInitializeResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_InitializeResponse); ok { + return x.InitializeResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRequiredExternalToolsRequest() *FrameworkServiceRequiredExternalToolsRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RequiredExternalToolsRequest); ok { + return x.RequiredExternalToolsRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRequiredExternalToolsResponse() *FrameworkServiceRequiredExternalToolsResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RequiredExternalToolsResponse); ok { + return x.RequiredExternalToolsResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRequirementsRequest() *FrameworkServiceRequirementsRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RequirementsRequest); ok { + return x.RequirementsRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRequirementsResponse() *FrameworkServiceRequirementsResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RequirementsResponse); ok { + return x.RequirementsResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRestoreRequest() *FrameworkServiceRestoreRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RestoreRequest); ok { + return x.RestoreRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetRestoreResponse() *FrameworkServiceRestoreResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_RestoreResponse); ok { + return x.RestoreResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetBuildRequest() *FrameworkServiceBuildRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_BuildRequest); ok { + return x.BuildRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetBuildResponse() *FrameworkServiceBuildResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_BuildResponse); ok { + return x.BuildResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetPackageRequest() *FrameworkServicePackageRequest { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_PackageRequest); ok { + return x.PackageRequest + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetPackageResponse() *FrameworkServicePackageResponse { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_PackageResponse); ok { + return x.PackageResponse + } + } + return nil +} + +func (x *FrameworkServiceMessage) GetProgressMessage() *FrameworkServiceProgressMessage { + if x != nil { + if x, ok := x.MessageType.(*FrameworkServiceMessage_ProgressMessage); ok { + return x.ProgressMessage + } + } + return nil +} + +type isFrameworkServiceMessage_MessageType interface { + isFrameworkServiceMessage_MessageType() +} + +type FrameworkServiceMessage_RegisterFrameworkServiceRequest struct { + RegisterFrameworkServiceRequest *RegisterFrameworkServiceRequest `protobuf:"bytes,2,opt,name=register_framework_service_request,json=registerFrameworkServiceRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_RegisterFrameworkServiceResponse struct { + RegisterFrameworkServiceResponse *RegisterFrameworkServiceResponse `protobuf:"bytes,3,opt,name=register_framework_service_response,json=registerFrameworkServiceResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_InitializeRequest struct { + InitializeRequest *FrameworkServiceInitializeRequest `protobuf:"bytes,4,opt,name=initialize_request,json=initializeRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_InitializeResponse struct { + InitializeResponse *FrameworkServiceInitializeResponse `protobuf:"bytes,5,opt,name=initialize_response,json=initializeResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_RequiredExternalToolsRequest struct { + RequiredExternalToolsRequest *FrameworkServiceRequiredExternalToolsRequest `protobuf:"bytes,6,opt,name=required_external_tools_request,json=requiredExternalToolsRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_RequiredExternalToolsResponse struct { + RequiredExternalToolsResponse *FrameworkServiceRequiredExternalToolsResponse `protobuf:"bytes,7,opt,name=required_external_tools_response,json=requiredExternalToolsResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_RequirementsRequest struct { + RequirementsRequest *FrameworkServiceRequirementsRequest `protobuf:"bytes,8,opt,name=requirements_request,json=requirementsRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_RequirementsResponse struct { + RequirementsResponse *FrameworkServiceRequirementsResponse `protobuf:"bytes,9,opt,name=requirements_response,json=requirementsResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_RestoreRequest struct { + RestoreRequest *FrameworkServiceRestoreRequest `protobuf:"bytes,10,opt,name=restore_request,json=restoreRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_RestoreResponse struct { + RestoreResponse *FrameworkServiceRestoreResponse `protobuf:"bytes,11,opt,name=restore_response,json=restoreResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_BuildRequest struct { + BuildRequest *FrameworkServiceBuildRequest `protobuf:"bytes,12,opt,name=build_request,json=buildRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_BuildResponse struct { + BuildResponse *FrameworkServiceBuildResponse `protobuf:"bytes,13,opt,name=build_response,json=buildResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_PackageRequest struct { + PackageRequest *FrameworkServicePackageRequest `protobuf:"bytes,14,opt,name=package_request,json=packageRequest,proto3,oneof"` +} + +type FrameworkServiceMessage_PackageResponse struct { + PackageResponse *FrameworkServicePackageResponse `protobuf:"bytes,15,opt,name=package_response,json=packageResponse,proto3,oneof"` +} + +type FrameworkServiceMessage_ProgressMessage struct { + ProgressMessage *FrameworkServiceProgressMessage `protobuf:"bytes,16,opt,name=progress_message,json=progressMessage,proto3,oneof"` +} + +func (*FrameworkServiceMessage_RegisterFrameworkServiceRequest) isFrameworkServiceMessage_MessageType() { +} + +func (*FrameworkServiceMessage_RegisterFrameworkServiceResponse) isFrameworkServiceMessage_MessageType() { +} + +func (*FrameworkServiceMessage_InitializeRequest) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_InitializeResponse) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_RequiredExternalToolsRequest) isFrameworkServiceMessage_MessageType() { +} + +func (*FrameworkServiceMessage_RequiredExternalToolsResponse) isFrameworkServiceMessage_MessageType() { +} + +func (*FrameworkServiceMessage_RequirementsRequest) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_RequirementsResponse) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_RestoreRequest) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_RestoreResponse) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_BuildRequest) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_BuildResponse) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_PackageRequest) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_PackageResponse) isFrameworkServiceMessage_MessageType() {} + +func (*FrameworkServiceMessage_ProgressMessage) isFrameworkServiceMessage_MessageType() {} + +// Error message for framework service operations +type FrameworkServiceErrorMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Details string `protobuf:"bytes,2,opt,name=details,proto3" json:"details,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceErrorMessage) Reset() { + *x = FrameworkServiceErrorMessage{} + mi := &file_framework_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceErrorMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceErrorMessage) ProtoMessage() {} + +func (x *FrameworkServiceErrorMessage) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 FrameworkServiceErrorMessage.ProtoReflect.Descriptor instead. +func (*FrameworkServiceErrorMessage) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{1} +} + +func (x *FrameworkServiceErrorMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *FrameworkServiceErrorMessage) GetDetails() string { + if x != nil { + return x.Details + } + return "" +} + +// Request to register a framework service provider +type RegisterFrameworkServiceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` // unique identifier for the language/framework (e.g., "rust", "go", "php") + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterFrameworkServiceRequest) Reset() { + *x = RegisterFrameworkServiceRequest{} + mi := &file_framework_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterFrameworkServiceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterFrameworkServiceRequest) ProtoMessage() {} + +func (x *RegisterFrameworkServiceRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 RegisterFrameworkServiceRequest.ProtoReflect.Descriptor instead. +func (*RegisterFrameworkServiceRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterFrameworkServiceRequest) GetLanguage() string { + if x != nil { + return x.Language + } + return "" +} + +type RegisterFrameworkServiceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterFrameworkServiceResponse) Reset() { + *x = RegisterFrameworkServiceResponse{} + mi := &file_framework_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterFrameworkServiceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterFrameworkServiceResponse) ProtoMessage() {} + +func (x *RegisterFrameworkServiceResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 RegisterFrameworkServiceResponse.ProtoReflect.Descriptor instead. +func (*RegisterFrameworkServiceResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{3} +} + +// Initialize request and response +type FrameworkServiceInitializeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServiceConfig *ServiceConfig `protobuf:"bytes,1,opt,name=service_config,json=serviceConfig,proto3" json:"service_config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceInitializeRequest) Reset() { + *x = FrameworkServiceInitializeRequest{} + mi := &file_framework_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceInitializeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceInitializeRequest) ProtoMessage() {} + +func (x *FrameworkServiceInitializeRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 FrameworkServiceInitializeRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServiceInitializeRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{4} +} + +func (x *FrameworkServiceInitializeRequest) GetServiceConfig() *ServiceConfig { + if x != nil { + return x.ServiceConfig + } + return nil +} + +type FrameworkServiceInitializeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceInitializeResponse) Reset() { + *x = FrameworkServiceInitializeResponse{} + mi := &file_framework_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceInitializeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceInitializeResponse) ProtoMessage() {} + +func (x *FrameworkServiceInitializeResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 FrameworkServiceInitializeResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServiceInitializeResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{5} +} + +// Required external tools request and response +type FrameworkServiceRequiredExternalToolsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServiceConfig *ServiceConfig `protobuf:"bytes,1,opt,name=service_config,json=serviceConfig,proto3" json:"service_config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRequiredExternalToolsRequest) Reset() { + *x = FrameworkServiceRequiredExternalToolsRequest{} + mi := &file_framework_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRequiredExternalToolsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRequiredExternalToolsRequest) ProtoMessage() {} + +func (x *FrameworkServiceRequiredExternalToolsRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_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 FrameworkServiceRequiredExternalToolsRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRequiredExternalToolsRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{6} +} + +func (x *FrameworkServiceRequiredExternalToolsRequest) GetServiceConfig() *ServiceConfig { + if x != nil { + return x.ServiceConfig + } + return nil +} + +type FrameworkServiceRequiredExternalToolsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tools []*ExternalTool `protobuf:"bytes,1,rep,name=tools,proto3" json:"tools,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRequiredExternalToolsResponse) Reset() { + *x = FrameworkServiceRequiredExternalToolsResponse{} + mi := &file_framework_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRequiredExternalToolsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRequiredExternalToolsResponse) ProtoMessage() {} + +func (x *FrameworkServiceRequiredExternalToolsResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[7] + 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 FrameworkServiceRequiredExternalToolsResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRequiredExternalToolsResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{7} +} + +func (x *FrameworkServiceRequiredExternalToolsResponse) GetTools() []*ExternalTool { + if x != nil { + return x.Tools + } + return nil +} + +// External tool definition +type ExternalTool struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + InstallUrl string `protobuf:"bytes,2,opt,name=install_url,json=installUrl,proto3" json:"install_url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExternalTool) Reset() { + *x = ExternalTool{} + mi := &file_framework_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExternalTool) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExternalTool) ProtoMessage() {} + +func (x *ExternalTool) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[8] + 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 ExternalTool.ProtoReflect.Descriptor instead. +func (*ExternalTool) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{8} +} + +func (x *ExternalTool) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ExternalTool) GetInstallUrl() string { + if x != nil { + return x.InstallUrl + } + return "" +} + +// Requirements request and response +type FrameworkServiceRequirementsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRequirementsRequest) Reset() { + *x = FrameworkServiceRequirementsRequest{} + mi := &file_framework_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRequirementsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRequirementsRequest) ProtoMessage() {} + +func (x *FrameworkServiceRequirementsRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[9] + 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 FrameworkServiceRequirementsRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRequirementsRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{9} +} + +type FrameworkServiceRequirementsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Requirements *FrameworkRequirements `protobuf:"bytes,1,opt,name=requirements,proto3" json:"requirements,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRequirementsResponse) Reset() { + *x = FrameworkServiceRequirementsResponse{} + mi := &file_framework_service_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRequirementsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRequirementsResponse) ProtoMessage() {} + +func (x *FrameworkServiceRequirementsResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[10] + 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 FrameworkServiceRequirementsResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRequirementsResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{10} +} + +func (x *FrameworkServiceRequirementsResponse) GetRequirements() *FrameworkRequirements { + if x != nil { + return x.Requirements + } + return nil +} + +// Framework requirements definition +type FrameworkRequirements struct { + state protoimpl.MessageState `protogen:"open.v1"` + Package *FrameworkPackageRequirements `protobuf:"bytes,1,opt,name=package,proto3" json:"package,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkRequirements) Reset() { + *x = FrameworkRequirements{} + mi := &file_framework_service_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkRequirements) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkRequirements) ProtoMessage() {} + +func (x *FrameworkRequirements) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[11] + 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 FrameworkRequirements.ProtoReflect.Descriptor instead. +func (*FrameworkRequirements) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{11} +} + +func (x *FrameworkRequirements) GetPackage() *FrameworkPackageRequirements { + if x != nil { + return x.Package + } + return nil +} + +type FrameworkPackageRequirements struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequireRestore bool `protobuf:"varint,1,opt,name=require_restore,json=requireRestore,proto3" json:"require_restore,omitempty"` + RequireBuild bool `protobuf:"varint,2,opt,name=require_build,json=requireBuild,proto3" json:"require_build,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkPackageRequirements) Reset() { + *x = FrameworkPackageRequirements{} + mi := &file_framework_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkPackageRequirements) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkPackageRequirements) ProtoMessage() {} + +func (x *FrameworkPackageRequirements) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[12] + 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 FrameworkPackageRequirements.ProtoReflect.Descriptor instead. +func (*FrameworkPackageRequirements) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{12} +} + +func (x *FrameworkPackageRequirements) GetRequireRestore() bool { + if x != nil { + return x.RequireRestore + } + return false +} + +func (x *FrameworkPackageRequirements) GetRequireBuild() bool { + if x != nil { + return x.RequireBuild + } + return false +} + +// Restore request and response +type FrameworkServiceRestoreRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServiceConfig *ServiceConfig `protobuf:"bytes,1,opt,name=service_config,json=serviceConfig,proto3" json:"service_config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRestoreRequest) Reset() { + *x = FrameworkServiceRestoreRequest{} + mi := &file_framework_service_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRestoreRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRestoreRequest) ProtoMessage() {} + +func (x *FrameworkServiceRestoreRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[13] + 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 FrameworkServiceRestoreRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRestoreRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{13} +} + +func (x *FrameworkServiceRestoreRequest) GetServiceConfig() *ServiceConfig { + if x != nil { + return x.ServiceConfig + } + return nil +} + +type FrameworkServiceRestoreResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + RestoreResult *ServiceRestoreResult `protobuf:"bytes,1,opt,name=restore_result,json=restoreResult,proto3" json:"restore_result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceRestoreResponse) Reset() { + *x = FrameworkServiceRestoreResponse{} + mi := &file_framework_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceRestoreResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceRestoreResponse) ProtoMessage() {} + +func (x *FrameworkServiceRestoreResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[14] + 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 FrameworkServiceRestoreResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServiceRestoreResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{14} +} + +func (x *FrameworkServiceRestoreResponse) GetRestoreResult() *ServiceRestoreResult { + if x != nil { + return x.RestoreResult + } + return nil +} + +// Service restore result +type ServiceRestoreResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Details map[string]string `protobuf:"bytes,1,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServiceRestoreResult) Reset() { + *x = ServiceRestoreResult{} + mi := &file_framework_service_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ServiceRestoreResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceRestoreResult) ProtoMessage() {} + +func (x *ServiceRestoreResult) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[15] + 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 ServiceRestoreResult.ProtoReflect.Descriptor instead. +func (*ServiceRestoreResult) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{15} +} + +func (x *ServiceRestoreResult) GetDetails() map[string]string { + if x != nil { + return x.Details + } + return nil +} + +// Build request and response +type FrameworkServiceBuildRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServiceConfig *ServiceConfig `protobuf:"bytes,1,opt,name=service_config,json=serviceConfig,proto3" json:"service_config,omitempty"` + RestoreOutput *ServiceRestoreResult `protobuf:"bytes,2,opt,name=restore_output,json=restoreOutput,proto3" json:"restore_output,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceBuildRequest) Reset() { + *x = FrameworkServiceBuildRequest{} + mi := &file_framework_service_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceBuildRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceBuildRequest) ProtoMessage() {} + +func (x *FrameworkServiceBuildRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[16] + 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 FrameworkServiceBuildRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServiceBuildRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{16} +} + +func (x *FrameworkServiceBuildRequest) GetServiceConfig() *ServiceConfig { + if x != nil { + return x.ServiceConfig + } + return nil +} + +func (x *FrameworkServiceBuildRequest) GetRestoreOutput() *ServiceRestoreResult { + if x != nil { + return x.RestoreOutput + } + return nil +} + +type FrameworkServiceBuildResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + BuildResult *ServiceBuildResult `protobuf:"bytes,1,opt,name=build_result,json=buildResult,proto3" json:"build_result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceBuildResponse) Reset() { + *x = FrameworkServiceBuildResponse{} + mi := &file_framework_service_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceBuildResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceBuildResponse) ProtoMessage() {} + +func (x *FrameworkServiceBuildResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[17] + 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 FrameworkServiceBuildResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServiceBuildResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{17} +} + +func (x *FrameworkServiceBuildResponse) GetBuildResult() *ServiceBuildResult { + if x != nil { + return x.BuildResult + } + return nil +} + +// Service build result +type ServiceBuildResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + Restore *ServiceRestoreResult `protobuf:"bytes,1,opt,name=restore,proto3" json:"restore,omitempty"` + Details map[string]string `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServiceBuildResult) Reset() { + *x = ServiceBuildResult{} + mi := &file_framework_service_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ServiceBuildResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceBuildResult) ProtoMessage() {} + +func (x *ServiceBuildResult) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[18] + 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 ServiceBuildResult.ProtoReflect.Descriptor instead. +func (*ServiceBuildResult) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{18} +} + +func (x *ServiceBuildResult) GetRestore() *ServiceRestoreResult { + if x != nil { + return x.Restore + } + return nil +} + +func (x *ServiceBuildResult) GetDetails() map[string]string { + if x != nil { + return x.Details + } + return nil +} + +// Package request and response +type FrameworkServicePackageRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ServiceConfig *ServiceConfig `protobuf:"bytes,1,opt,name=service_config,json=serviceConfig,proto3" json:"service_config,omitempty"` + BuildOutput *ServiceBuildResult `protobuf:"bytes,2,opt,name=build_output,json=buildOutput,proto3" json:"build_output,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServicePackageRequest) Reset() { + *x = FrameworkServicePackageRequest{} + mi := &file_framework_service_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServicePackageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServicePackageRequest) ProtoMessage() {} + +func (x *FrameworkServicePackageRequest) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[19] + 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 FrameworkServicePackageRequest.ProtoReflect.Descriptor instead. +func (*FrameworkServicePackageRequest) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{19} +} + +func (x *FrameworkServicePackageRequest) GetServiceConfig() *ServiceConfig { + if x != nil { + return x.ServiceConfig + } + return nil +} + +func (x *FrameworkServicePackageRequest) GetBuildOutput() *ServiceBuildResult { + if x != nil { + return x.BuildOutput + } + return nil +} + +type FrameworkServicePackageResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + PackageResult *ServicePackageResult `protobuf:"bytes,1,opt,name=package_result,json=packageResult,proto3" json:"package_result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServicePackageResponse) Reset() { + *x = FrameworkServicePackageResponse{} + mi := &file_framework_service_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServicePackageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServicePackageResponse) ProtoMessage() {} + +func (x *FrameworkServicePackageResponse) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[20] + 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 FrameworkServicePackageResponse.ProtoReflect.Descriptor instead. +func (*FrameworkServicePackageResponse) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{20} +} + +func (x *FrameworkServicePackageResponse) GetPackageResult() *ServicePackageResult { + if x != nil { + return x.PackageResult + } + return nil +} + +// Progress message for framework service operations +type FrameworkServiceProgressMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp in milliseconds + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FrameworkServiceProgressMessage) Reset() { + *x = FrameworkServiceProgressMessage{} + mi := &file_framework_service_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FrameworkServiceProgressMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FrameworkServiceProgressMessage) ProtoMessage() {} + +func (x *FrameworkServiceProgressMessage) ProtoReflect() protoreflect.Message { + mi := &file_framework_service_proto_msgTypes[21] + 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 FrameworkServiceProgressMessage.ProtoReflect.Descriptor instead. +func (*FrameworkServiceProgressMessage) Descriptor() ([]byte, []int) { + return file_framework_service_proto_rawDescGZIP(), []int{21} +} + +func (x *FrameworkServiceProgressMessage) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *FrameworkServiceProgressMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *FrameworkServiceProgressMessage) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +var File_framework_service_proto protoreflect.FileDescriptor + +const file_framework_service_proto_rawDesc = "" + + "\n" + + "\x17framework_service.proto\x12\x06azdext\x1a\fmodels.proto\x1a\x14service_target.proto\"\xc0\f\n" + + "\x17FrameworkServiceMessage\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12:\n" + + "\x05error\x18c \x01(\v2$.azdext.FrameworkServiceErrorMessageR\x05error\x12v\n" + + "\"register_framework_service_request\x18\x02 \x01(\v2'.azdext.RegisterFrameworkServiceRequestH\x00R\x1fregisterFrameworkServiceRequest\x12y\n" + + "#register_framework_service_response\x18\x03 \x01(\v2(.azdext.RegisterFrameworkServiceResponseH\x00R registerFrameworkServiceResponse\x12Z\n" + + "\x12initialize_request\x18\x04 \x01(\v2).azdext.FrameworkServiceInitializeRequestH\x00R\x11initializeRequest\x12]\n" + + "\x13initialize_response\x18\x05 \x01(\v2*.azdext.FrameworkServiceInitializeResponseH\x00R\x12initializeResponse\x12}\n" + + "\x1frequired_external_tools_request\x18\x06 \x01(\v24.azdext.FrameworkServiceRequiredExternalToolsRequestH\x00R\x1crequiredExternalToolsRequest\x12\x80\x01\n" + + " required_external_tools_response\x18\a \x01(\v25.azdext.FrameworkServiceRequiredExternalToolsResponseH\x00R\x1drequiredExternalToolsResponse\x12`\n" + + "\x14requirements_request\x18\b \x01(\v2+.azdext.FrameworkServiceRequirementsRequestH\x00R\x13requirementsRequest\x12c\n" + + "\x15requirements_response\x18\t \x01(\v2,.azdext.FrameworkServiceRequirementsResponseH\x00R\x14requirementsResponse\x12Q\n" + + "\x0frestore_request\x18\n" + + " \x01(\v2&.azdext.FrameworkServiceRestoreRequestH\x00R\x0erestoreRequest\x12T\n" + + "\x10restore_response\x18\v \x01(\v2'.azdext.FrameworkServiceRestoreResponseH\x00R\x0frestoreResponse\x12K\n" + + "\rbuild_request\x18\f \x01(\v2$.azdext.FrameworkServiceBuildRequestH\x00R\fbuildRequest\x12N\n" + + "\x0ebuild_response\x18\r \x01(\v2%.azdext.FrameworkServiceBuildResponseH\x00R\rbuildResponse\x12Q\n" + + "\x0fpackage_request\x18\x0e \x01(\v2&.azdext.FrameworkServicePackageRequestH\x00R\x0epackageRequest\x12T\n" + + "\x10package_response\x18\x0f \x01(\v2'.azdext.FrameworkServicePackageResponseH\x00R\x0fpackageResponse\x12T\n" + + "\x10progress_message\x18\x10 \x01(\v2'.azdext.FrameworkServiceProgressMessageH\x00R\x0fprogressMessageB\x0e\n" + + "\fmessage_type\"R\n" + + "\x1cFrameworkServiceErrorMessage\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\x12\x18\n" + + "\adetails\x18\x02 \x01(\tR\adetails\"=\n" + + "\x1fRegisterFrameworkServiceRequest\x12\x1a\n" + + "\blanguage\x18\x01 \x01(\tR\blanguage\"\"\n" + + " RegisterFrameworkServiceResponse\"a\n" + + "!FrameworkServiceInitializeRequest\x12<\n" + + "\x0eservice_config\x18\x01 \x01(\v2\x15.azdext.ServiceConfigR\rserviceConfig\"$\n" + + "\"FrameworkServiceInitializeResponse\"l\n" + + ",FrameworkServiceRequiredExternalToolsRequest\x12<\n" + + "\x0eservice_config\x18\x01 \x01(\v2\x15.azdext.ServiceConfigR\rserviceConfig\"[\n" + + "-FrameworkServiceRequiredExternalToolsResponse\x12*\n" + + "\x05tools\x18\x01 \x03(\v2\x14.azdext.ExternalToolR\x05tools\"C\n" + + "\fExternalTool\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n" + + "\vinstall_url\x18\x02 \x01(\tR\n" + + "installUrl\"%\n" + + "#FrameworkServiceRequirementsRequest\"i\n" + + "$FrameworkServiceRequirementsResponse\x12A\n" + + "\frequirements\x18\x01 \x01(\v2\x1d.azdext.FrameworkRequirementsR\frequirements\"W\n" + + "\x15FrameworkRequirements\x12>\n" + + "\apackage\x18\x01 \x01(\v2$.azdext.FrameworkPackageRequirementsR\apackage\"l\n" + + "\x1cFrameworkPackageRequirements\x12'\n" + + "\x0frequire_restore\x18\x01 \x01(\bR\x0erequireRestore\x12#\n" + + "\rrequire_build\x18\x02 \x01(\bR\frequireBuild\"^\n" + + "\x1eFrameworkServiceRestoreRequest\x12<\n" + + "\x0eservice_config\x18\x01 \x01(\v2\x15.azdext.ServiceConfigR\rserviceConfig\"f\n" + + "\x1fFrameworkServiceRestoreResponse\x12C\n" + + "\x0erestore_result\x18\x01 \x01(\v2\x1c.azdext.ServiceRestoreResultR\rrestoreResult\"\x97\x01\n" + + "\x14ServiceRestoreResult\x12C\n" + + "\adetails\x18\x01 \x03(\v2).azdext.ServiceRestoreResult.DetailsEntryR\adetails\x1a:\n" + + "\fDetailsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa1\x01\n" + + "\x1cFrameworkServiceBuildRequest\x12<\n" + + "\x0eservice_config\x18\x01 \x01(\v2\x15.azdext.ServiceConfigR\rserviceConfig\x12C\n" + + "\x0erestore_output\x18\x02 \x01(\v2\x1c.azdext.ServiceRestoreResultR\rrestoreOutput\"^\n" + + "\x1dFrameworkServiceBuildResponse\x12=\n" + + "\fbuild_result\x18\x01 \x01(\v2\x1a.azdext.ServiceBuildResultR\vbuildResult\"\xcb\x01\n" + + "\x12ServiceBuildResult\x126\n" + + "\arestore\x18\x01 \x01(\v2\x1c.azdext.ServiceRestoreResultR\arestore\x12A\n" + + "\adetails\x18\x02 \x03(\v2'.azdext.ServiceBuildResult.DetailsEntryR\adetails\x1a:\n" + + "\fDetailsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x9d\x01\n" + + "\x1eFrameworkServicePackageRequest\x12<\n" + + "\x0eservice_config\x18\x01 \x01(\v2\x15.azdext.ServiceConfigR\rserviceConfig\x12=\n" + + "\fbuild_output\x18\x02 \x01(\v2\x1a.azdext.ServiceBuildResultR\vbuildOutput\"f\n" + + "\x1fFrameworkServicePackageResponse\x12C\n" + + "\x0epackage_result\x18\x01 \x01(\v2\x1c.azdext.ServicePackageResultR\rpackageResult\"x\n" + + "\x1fFrameworkServiceProgressMessage\x12\x1d\n" + + "\n" + + "request_id\x18\x01 \x01(\tR\trequestId\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\x12\x1c\n" + + "\ttimestamp\x18\x03 \x01(\x03R\ttimestamp2b\n" + + "\x10FrameworkService\x12N\n" + + "\x06Stream\x12\x1f.azdext.FrameworkServiceMessage\x1a\x1f.azdext.FrameworkServiceMessage(\x010\x01B/Z-github.com/azure/azure-dev/cli/azd/pkg/azdextb\x06proto3" + +var ( + file_framework_service_proto_rawDescOnce sync.Once + file_framework_service_proto_rawDescData []byte +) + +func file_framework_service_proto_rawDescGZIP() []byte { + file_framework_service_proto_rawDescOnce.Do(func() { + file_framework_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_framework_service_proto_rawDesc), len(file_framework_service_proto_rawDesc))) + }) + return file_framework_service_proto_rawDescData +} + +var file_framework_service_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_framework_service_proto_goTypes = []any{ + (*FrameworkServiceMessage)(nil), // 0: azdext.FrameworkServiceMessage + (*FrameworkServiceErrorMessage)(nil), // 1: azdext.FrameworkServiceErrorMessage + (*RegisterFrameworkServiceRequest)(nil), // 2: azdext.RegisterFrameworkServiceRequest + (*RegisterFrameworkServiceResponse)(nil), // 3: azdext.RegisterFrameworkServiceResponse + (*FrameworkServiceInitializeRequest)(nil), // 4: azdext.FrameworkServiceInitializeRequest + (*FrameworkServiceInitializeResponse)(nil), // 5: azdext.FrameworkServiceInitializeResponse + (*FrameworkServiceRequiredExternalToolsRequest)(nil), // 6: azdext.FrameworkServiceRequiredExternalToolsRequest + (*FrameworkServiceRequiredExternalToolsResponse)(nil), // 7: azdext.FrameworkServiceRequiredExternalToolsResponse + (*ExternalTool)(nil), // 8: azdext.ExternalTool + (*FrameworkServiceRequirementsRequest)(nil), // 9: azdext.FrameworkServiceRequirementsRequest + (*FrameworkServiceRequirementsResponse)(nil), // 10: azdext.FrameworkServiceRequirementsResponse + (*FrameworkRequirements)(nil), // 11: azdext.FrameworkRequirements + (*FrameworkPackageRequirements)(nil), // 12: azdext.FrameworkPackageRequirements + (*FrameworkServiceRestoreRequest)(nil), // 13: azdext.FrameworkServiceRestoreRequest + (*FrameworkServiceRestoreResponse)(nil), // 14: azdext.FrameworkServiceRestoreResponse + (*ServiceRestoreResult)(nil), // 15: azdext.ServiceRestoreResult + (*FrameworkServiceBuildRequest)(nil), // 16: azdext.FrameworkServiceBuildRequest + (*FrameworkServiceBuildResponse)(nil), // 17: azdext.FrameworkServiceBuildResponse + (*ServiceBuildResult)(nil), // 18: azdext.ServiceBuildResult + (*FrameworkServicePackageRequest)(nil), // 19: azdext.FrameworkServicePackageRequest + (*FrameworkServicePackageResponse)(nil), // 20: azdext.FrameworkServicePackageResponse + (*FrameworkServiceProgressMessage)(nil), // 21: azdext.FrameworkServiceProgressMessage + nil, // 22: azdext.ServiceRestoreResult.DetailsEntry + nil, // 23: azdext.ServiceBuildResult.DetailsEntry + (*ServiceConfig)(nil), // 24: azdext.ServiceConfig + (*ServicePackageResult)(nil), // 25: azdext.ServicePackageResult +} +var file_framework_service_proto_depIdxs = []int32{ + 1, // 0: azdext.FrameworkServiceMessage.error:type_name -> azdext.FrameworkServiceErrorMessage + 2, // 1: azdext.FrameworkServiceMessage.register_framework_service_request:type_name -> azdext.RegisterFrameworkServiceRequest + 3, // 2: azdext.FrameworkServiceMessage.register_framework_service_response:type_name -> azdext.RegisterFrameworkServiceResponse + 4, // 3: azdext.FrameworkServiceMessage.initialize_request:type_name -> azdext.FrameworkServiceInitializeRequest + 5, // 4: azdext.FrameworkServiceMessage.initialize_response:type_name -> azdext.FrameworkServiceInitializeResponse + 6, // 5: azdext.FrameworkServiceMessage.required_external_tools_request:type_name -> azdext.FrameworkServiceRequiredExternalToolsRequest + 7, // 6: azdext.FrameworkServiceMessage.required_external_tools_response:type_name -> azdext.FrameworkServiceRequiredExternalToolsResponse + 9, // 7: azdext.FrameworkServiceMessage.requirements_request:type_name -> azdext.FrameworkServiceRequirementsRequest + 10, // 8: azdext.FrameworkServiceMessage.requirements_response:type_name -> azdext.FrameworkServiceRequirementsResponse + 13, // 9: azdext.FrameworkServiceMessage.restore_request:type_name -> azdext.FrameworkServiceRestoreRequest + 14, // 10: azdext.FrameworkServiceMessage.restore_response:type_name -> azdext.FrameworkServiceRestoreResponse + 16, // 11: azdext.FrameworkServiceMessage.build_request:type_name -> azdext.FrameworkServiceBuildRequest + 17, // 12: azdext.FrameworkServiceMessage.build_response:type_name -> azdext.FrameworkServiceBuildResponse + 19, // 13: azdext.FrameworkServiceMessage.package_request:type_name -> azdext.FrameworkServicePackageRequest + 20, // 14: azdext.FrameworkServiceMessage.package_response:type_name -> azdext.FrameworkServicePackageResponse + 21, // 15: azdext.FrameworkServiceMessage.progress_message:type_name -> azdext.FrameworkServiceProgressMessage + 24, // 16: azdext.FrameworkServiceInitializeRequest.service_config:type_name -> azdext.ServiceConfig + 24, // 17: azdext.FrameworkServiceRequiredExternalToolsRequest.service_config:type_name -> azdext.ServiceConfig + 8, // 18: azdext.FrameworkServiceRequiredExternalToolsResponse.tools:type_name -> azdext.ExternalTool + 11, // 19: azdext.FrameworkServiceRequirementsResponse.requirements:type_name -> azdext.FrameworkRequirements + 12, // 20: azdext.FrameworkRequirements.package:type_name -> azdext.FrameworkPackageRequirements + 24, // 21: azdext.FrameworkServiceRestoreRequest.service_config:type_name -> azdext.ServiceConfig + 15, // 22: azdext.FrameworkServiceRestoreResponse.restore_result:type_name -> azdext.ServiceRestoreResult + 22, // 23: azdext.ServiceRestoreResult.details:type_name -> azdext.ServiceRestoreResult.DetailsEntry + 24, // 24: azdext.FrameworkServiceBuildRequest.service_config:type_name -> azdext.ServiceConfig + 15, // 25: azdext.FrameworkServiceBuildRequest.restore_output:type_name -> azdext.ServiceRestoreResult + 18, // 26: azdext.FrameworkServiceBuildResponse.build_result:type_name -> azdext.ServiceBuildResult + 15, // 27: azdext.ServiceBuildResult.restore:type_name -> azdext.ServiceRestoreResult + 23, // 28: azdext.ServiceBuildResult.details:type_name -> azdext.ServiceBuildResult.DetailsEntry + 24, // 29: azdext.FrameworkServicePackageRequest.service_config:type_name -> azdext.ServiceConfig + 18, // 30: azdext.FrameworkServicePackageRequest.build_output:type_name -> azdext.ServiceBuildResult + 25, // 31: azdext.FrameworkServicePackageResponse.package_result:type_name -> azdext.ServicePackageResult + 0, // 32: azdext.FrameworkService.Stream:input_type -> azdext.FrameworkServiceMessage + 0, // 33: azdext.FrameworkService.Stream:output_type -> azdext.FrameworkServiceMessage + 33, // [33:34] is the sub-list for method output_type + 32, // [32:33] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name +} + +func init() { file_framework_service_proto_init() } +func file_framework_service_proto_init() { + if File_framework_service_proto != nil { + return + } + file_models_proto_init() + file_service_target_proto_init() + file_framework_service_proto_msgTypes[0].OneofWrappers = []any{ + (*FrameworkServiceMessage_RegisterFrameworkServiceRequest)(nil), + (*FrameworkServiceMessage_RegisterFrameworkServiceResponse)(nil), + (*FrameworkServiceMessage_InitializeRequest)(nil), + (*FrameworkServiceMessage_InitializeResponse)(nil), + (*FrameworkServiceMessage_RequiredExternalToolsRequest)(nil), + (*FrameworkServiceMessage_RequiredExternalToolsResponse)(nil), + (*FrameworkServiceMessage_RequirementsRequest)(nil), + (*FrameworkServiceMessage_RequirementsResponse)(nil), + (*FrameworkServiceMessage_RestoreRequest)(nil), + (*FrameworkServiceMessage_RestoreResponse)(nil), + (*FrameworkServiceMessage_BuildRequest)(nil), + (*FrameworkServiceMessage_BuildResponse)(nil), + (*FrameworkServiceMessage_PackageRequest)(nil), + (*FrameworkServiceMessage_PackageResponse)(nil), + (*FrameworkServiceMessage_ProgressMessage)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_framework_service_proto_rawDesc), len(file_framework_service_proto_rawDesc)), + NumEnums: 0, + NumMessages: 24, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_framework_service_proto_goTypes, + DependencyIndexes: file_framework_service_proto_depIdxs, + MessageInfos: file_framework_service_proto_msgTypes, + }.Build() + File_framework_service_proto = out.File + file_framework_service_proto_goTypes = nil + file_framework_service_proto_depIdxs = nil +} diff --git a/cli/azd/pkg/azdext/framework_service_grpc.pb.go b/cli/azd/pkg/azdext/framework_service_grpc.pb.go new file mode 100644 index 00000000000..784f60d93fd --- /dev/null +++ b/cli/azd/pkg/azdext/framework_service_grpc.pb.go @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v6.32.1 +// source: framework_service.proto + +package azdext + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + FrameworkService_Stream_FullMethodName = "/azdext.FrameworkService/Stream" +) + +// FrameworkServiceClient is the client API for FrameworkService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FrameworkServiceClient interface { + // Bidirectional stream for framework service requests and responses + Stream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[FrameworkServiceMessage, FrameworkServiceMessage], error) +} + +type frameworkServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFrameworkServiceClient(cc grpc.ClientConnInterface) FrameworkServiceClient { + return &frameworkServiceClient{cc} +} + +func (c *frameworkServiceClient) Stream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[FrameworkServiceMessage, FrameworkServiceMessage], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &FrameworkService_ServiceDesc.Streams[0], FrameworkService_Stream_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[FrameworkServiceMessage, FrameworkServiceMessage]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type FrameworkService_StreamClient = grpc.BidiStreamingClient[FrameworkServiceMessage, FrameworkServiceMessage] + +// FrameworkServiceServer is the server API for FrameworkService service. +// All implementations must embed UnimplementedFrameworkServiceServer +// for forward compatibility. +type FrameworkServiceServer interface { + // Bidirectional stream for framework service requests and responses + Stream(grpc.BidiStreamingServer[FrameworkServiceMessage, FrameworkServiceMessage]) error + mustEmbedUnimplementedFrameworkServiceServer() +} + +// UnimplementedFrameworkServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedFrameworkServiceServer struct{} + +func (UnimplementedFrameworkServiceServer) Stream(grpc.BidiStreamingServer[FrameworkServiceMessage, FrameworkServiceMessage]) error { + return status.Errorf(codes.Unimplemented, "method Stream not implemented") +} +func (UnimplementedFrameworkServiceServer) mustEmbedUnimplementedFrameworkServiceServer() {} +func (UnimplementedFrameworkServiceServer) testEmbeddedByValue() {} + +// UnsafeFrameworkServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FrameworkServiceServer will +// result in compilation errors. +type UnsafeFrameworkServiceServer interface { + mustEmbedUnimplementedFrameworkServiceServer() +} + +func RegisterFrameworkServiceServer(s grpc.ServiceRegistrar, srv FrameworkServiceServer) { + // If the following call pancis, it indicates UnimplementedFrameworkServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&FrameworkService_ServiceDesc, srv) +} + +func _FrameworkService_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(FrameworkServiceServer).Stream(&grpc.GenericServerStream[FrameworkServiceMessage, FrameworkServiceMessage]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type FrameworkService_StreamServer = grpc.BidiStreamingServer[FrameworkServiceMessage, FrameworkServiceMessage] + +// FrameworkService_ServiceDesc is the grpc.ServiceDesc for FrameworkService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FrameworkService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "azdext.FrameworkService", + HandlerType: (*FrameworkServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Stream", + Handler: _FrameworkService_Stream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "framework_service.proto", +} diff --git a/cli/azd/pkg/azdext/models.pb.go b/cli/azd/pkg/azdext/models.pb.go index f7545a074aa..817205c1016 100644 --- a/cli/azd/pkg/azdext/models.pb.go +++ b/cli/azd/pkg/azdext/models.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: models.proto package azdext diff --git a/cli/azd/pkg/azdext/project.pb.go b/cli/azd/pkg/azdext/project.pb.go index d8fec1e1c11..5dc524bc5f2 100644 --- a/cli/azd/pkg/azdext/project.pb.go +++ b/cli/azd/pkg/azdext/project.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: project.proto package azdext diff --git a/cli/azd/pkg/azdext/project_grpc.pb.go b/cli/azd/pkg/azdext/project_grpc.pb.go index 1ac4b4de0cc..95824d4a316 100644 --- a/cli/azd/pkg/azdext/project_grpc.pb.go +++ b/cli/azd/pkg/azdext/project_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: project.proto package azdext diff --git a/cli/azd/pkg/azdext/prompt.pb.go b/cli/azd/pkg/azdext/prompt.pb.go index d1788a446d0..2c7829b1cb5 100644 --- a/cli/azd/pkg/azdext/prompt.pb.go +++ b/cli/azd/pkg/azdext/prompt.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: prompt.proto package azdext diff --git a/cli/azd/pkg/azdext/prompt_grpc.pb.go b/cli/azd/pkg/azdext/prompt_grpc.pb.go index bf3c5637cfb..93b804b49d5 100644 --- a/cli/azd/pkg/azdext/prompt_grpc.pb.go +++ b/cli/azd/pkg/azdext/prompt_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: prompt.proto package azdext diff --git a/cli/azd/pkg/azdext/service_target.pb.go b/cli/azd/pkg/azdext/service_target.pb.go index 0a436fbe8fb..3141dd02508 100644 --- a/cli/azd/pkg/azdext/service_target.pb.go +++ b/cli/azd/pkg/azdext/service_target.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: service_target.proto package azdext diff --git a/cli/azd/pkg/azdext/service_target_grpc.pb.go b/cli/azd/pkg/azdext/service_target_grpc.pb.go index 3f78fd2ac31..8c0de6e86c7 100644 --- a/cli/azd/pkg/azdext/service_target_grpc.pb.go +++ b/cli/azd/pkg/azdext/service_target_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: service_target.proto package azdext diff --git a/cli/azd/pkg/azdext/service_target_manager.go b/cli/azd/pkg/azdext/service_target_manager.go index aea8bcaa485..929c509c1c5 100644 --- a/cli/azd/pkg/azdext/service_target_manager.go +++ b/cli/azd/pkg/azdext/service_target_manager.go @@ -6,6 +6,7 @@ package azdext import ( "context" "errors" + "fmt" "io" "log" "time" @@ -18,6 +19,30 @@ import ( // ProgressReporter defines a function type for reporting progress updates from extensions type ProgressReporter func(message string) +// FrameworkServiceProvider defines the interface for framework service logic. +type FrameworkServiceProvider interface { + Initialize(ctx context.Context, serviceConfig *ServiceConfig) error + RequiredExternalTools(ctx context.Context, serviceConfig *ServiceConfig) ([]*ExternalTool, error) + Requirements() (*FrameworkRequirements, error) + Restore( + ctx context.Context, + serviceConfig *ServiceConfig, + progress ProgressReporter, + ) (*ServiceRestoreResult, error) + Build( + ctx context.Context, + serviceConfig *ServiceConfig, + restoreOutput *ServiceRestoreResult, + progress ProgressReporter, + ) (*ServiceBuildResult, error) + Package( + ctx context.Context, + serviceConfig *ServiceConfig, + buildOutput *ServiceBuildResult, + progress ProgressReporter, + ) (*ServicePackageResult, error) +} + // ServiceTargetProvider defines the interface for service target logic. type ServiceTargetProvider interface { Initialize(ctx context.Context, serviceConfig *ServiceConfig) error @@ -56,6 +81,304 @@ type ServiceTargetProvider interface { ) (*ServiceDeployResult, error) } +// FrameworkServiceManager handles registration and request forwarding for a framework service provider. +type FrameworkServiceManager struct { + client *AzdClient + stream FrameworkService_StreamClient +} + +// NewFrameworkServiceManager creates a new FrameworkServiceManager for an AzdClient. +func NewFrameworkServiceManager(client *AzdClient) *FrameworkServiceManager { + return &FrameworkServiceManager{ + client: client, + } +} + +// Register registers a framework service provider with the specified language name. +func (m *FrameworkServiceManager) Register(ctx context.Context, provider FrameworkServiceProvider, language string) error { + client := m.client.FrameworkService() + stream, err := client.Stream(ctx) + if err != nil { + return err + } + + m.stream = stream + + // Send registration request + err = stream.Send(&FrameworkServiceMessage{ + RequestId: "register", + MessageType: &FrameworkServiceMessage_RegisterFrameworkServiceRequest{ + RegisterFrameworkServiceRequest: &RegisterFrameworkServiceRequest{ + Language: language, + }, + }, + }) + if err != nil { + return err + } + + // Wait for registration response + resp, err := stream.Recv() + if err != nil { + return err + } + + if resp.Error != nil { + return fmt.Errorf("framework service registration error: %s", resp.Error.Message) + } + + if resp.GetRegisterFrameworkServiceResponse() == nil { + return fmt.Errorf("expected RegisterFrameworkServiceResponse, got %T", resp.GetMessageType()) + } + + // Start handling the framework service stream + + // Add a small delay to ensure the stream handler is ready before the server can use the stream + ready := make(chan struct{}) + go func() { + close(ready) // Signal that we're about to start + m.handleFrameworkServiceStream(ctx, provider) + }() + <-ready // Wait for the goroutine to start + + return nil +} + +// handleFrameworkServiceStream handles the bidirectional stream for framework service operations +func (m *FrameworkServiceManager) handleFrameworkServiceStream(ctx context.Context, provider FrameworkServiceProvider) { + for { + select { + case <-ctx.Done(): + log.Println("Context cancelled by caller, exiting framework service stream") + return + default: + msg, err := m.stream.Recv() + if err != nil { + log.Printf("framework service stream closed: %v", err) + return + } + // Process message synchronously to avoid race condition with stream.Recv() + resp := m.buildFrameworkServiceResponseMsg(ctx, provider, msg) + if resp != nil { + if err := m.stream.Send(resp); err != nil { + log.Printf("failed to send framework service response: %v", err) + } else { + // Don't immediately go back to stream.Recv() - let the receiver process first + time.Sleep(200 * time.Millisecond) + } + } else { + log.Printf("buildFrameworkServiceResponseMsg returned nil response") + } + } + } +} + +// Close closes the framework service manager stream. +func (m *FrameworkServiceManager) Close() error { + if m.stream != nil { + return m.stream.CloseSend() + } + return nil +} + +// buildFrameworkServiceResponseMsg handles individual framework service requests and builds responses +func (m *FrameworkServiceManager) buildFrameworkServiceResponseMsg( + ctx context.Context, + provider FrameworkServiceProvider, + msg *FrameworkServiceMessage, +) *FrameworkServiceMessage { + var resp *FrameworkServiceMessage + switch r := msg.MessageType.(type) { + case *FrameworkServiceMessage_InitializeRequest: + initReq := r.InitializeRequest + var serviceConfig *ServiceConfig + if initReq != nil { + serviceConfig = initReq.ServiceConfig + } + + err := provider.Initialize(ctx, serviceConfig) + + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_InitializeResponse{ + InitializeResponse: &FrameworkServiceInitializeResponse{}, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + case *FrameworkServiceMessage_RequiredExternalToolsRequest: + + reqReq := r.RequiredExternalToolsRequest + var serviceConfig *ServiceConfig + if reqReq != nil { + serviceConfig = reqReq.ServiceConfig + } + + tools, err := provider.RequiredExternalTools(ctx, serviceConfig) + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_RequiredExternalToolsResponse{ + RequiredExternalToolsResponse: &FrameworkServiceRequiredExternalToolsResponse{ + Tools: tools, + }, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + case *FrameworkServiceMessage_RequirementsRequest: + requirements, err := provider.Requirements() + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_RequirementsResponse{ + RequirementsResponse: &FrameworkServiceRequirementsResponse{ + Requirements: requirements, + }, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + case *FrameworkServiceMessage_RestoreRequest: + progressReporter := func(message string) { + progressMsg := &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_ProgressMessage{ + ProgressMessage: &FrameworkServiceProgressMessage{ + RequestId: msg.RequestId, + Message: message, + Timestamp: time.Now().UnixMilli(), + }, + }, + } + if err := m.stream.Send(progressMsg); err != nil { + log.Printf("failed to send progress message: %v", err) + } + } + + restoreReq := r.RestoreRequest + var serviceConfig *ServiceConfig + if restoreReq != nil { + serviceConfig = restoreReq.ServiceConfig + } + + result, err := provider.Restore(ctx, serviceConfig, progressReporter) + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_RestoreResponse{ + RestoreResponse: &FrameworkServiceRestoreResponse{ + RestoreResult: result, + }, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + case *FrameworkServiceMessage_BuildRequest: + progressReporter := func(message string) { + progressMsg := &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_ProgressMessage{ + ProgressMessage: &FrameworkServiceProgressMessage{ + RequestId: msg.RequestId, + Message: message, + Timestamp: time.Now().UnixMilli(), + }, + }, + } + if err := m.stream.Send(progressMsg); err != nil { + log.Printf("failed to send progress message: %v", err) + } + } + + buildReq := r.BuildRequest + var serviceConfig *ServiceConfig + var restoreOutput *ServiceRestoreResult + if buildReq != nil { + serviceConfig = buildReq.ServiceConfig + restoreOutput = buildReq.RestoreOutput + } + + result, err := provider.Build(ctx, serviceConfig, restoreOutput, progressReporter) + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_BuildResponse{ + BuildResponse: &FrameworkServiceBuildResponse{ + BuildResult: result, + }, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + case *FrameworkServiceMessage_PackageRequest: + progressReporter := func(message string) { + progressMsg := &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_ProgressMessage{ + ProgressMessage: &FrameworkServiceProgressMessage{ + RequestId: msg.RequestId, + Message: message, + Timestamp: time.Now().UnixMilli(), + }, + }, + } + if err := m.stream.Send(progressMsg); err != nil { + log.Printf("failed to send progress message: %v", err) + } + } + + packageReq := r.PackageRequest + var serviceConfig *ServiceConfig + var buildOutput *ServiceBuildResult + if packageReq != nil { + serviceConfig = packageReq.ServiceConfig + buildOutput = packageReq.BuildOutput + } + + result, err := provider.Package(ctx, serviceConfig, buildOutput, progressReporter) + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + MessageType: &FrameworkServiceMessage_PackageResponse{ + PackageResponse: &FrameworkServicePackageResponse{ + PackageResult: result, + }, + }, + } + if err != nil { + resp.Error = &FrameworkServiceErrorMessage{ + Message: err.Error(), + } + } + + default: + resp = &FrameworkServiceMessage{ + RequestId: msg.RequestId, + Error: &FrameworkServiceErrorMessage{ + Message: fmt.Sprintf("unsupported message type: %T", r), + }, + } + } + + return resp +} + // ServiceTargetManager handles registration and provisioning request forwarding for a provider. type ServiceTargetManager struct { client *AzdClient diff --git a/cli/azd/pkg/azdext/user_config.pb.go b/cli/azd/pkg/azdext/user_config.pb.go index be0e9263388..3b093f24f04 100644 --- a/cli/azd/pkg/azdext/user_config.pb.go +++ b/cli/azd/pkg/azdext/user_config.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: user_config.proto package azdext diff --git a/cli/azd/pkg/azdext/user_config_grpc.pb.go b/cli/azd/pkg/azdext/user_config_grpc.pb.go index 43f50590363..7fd5b67bbcd 100644 --- a/cli/azd/pkg/azdext/user_config_grpc.pb.go +++ b/cli/azd/pkg/azdext/user_config_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: user_config.proto package azdext diff --git a/cli/azd/pkg/azdext/workflow.pb.go b/cli/azd/pkg/azdext/workflow.pb.go index 1a35e67cb59..e4f0a55bc0a 100644 --- a/cli/azd/pkg/azdext/workflow.pb.go +++ b/cli/azd/pkg/azdext/workflow.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v6.32.0 +// protoc-gen-go v1.36.10 +// protoc v6.32.1 // source: workflow.proto package azdext diff --git a/cli/azd/pkg/azdext/workflow_grpc.pb.go b/cli/azd/pkg/azdext/workflow_grpc.pb.go index 49b3c3a0dfd..221273b8648 100644 --- a/cli/azd/pkg/azdext/workflow_grpc.pb.go +++ b/cli/azd/pkg/azdext/workflow_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v6.32.0 +// - protoc v6.32.1 // source: workflow.proto package azdext diff --git a/cli/azd/pkg/extensions/registry.go b/cli/azd/pkg/extensions/registry.go index f0042c00cac..8b39342abce 100644 --- a/cli/azd/pkg/extensions/registry.go +++ b/cli/azd/pkg/extensions/registry.go @@ -38,6 +38,8 @@ const ( McpServerCapability CapabilityType = "mcp-server" // Service target providers enable extensions to package, publish, and deploy to custom service targets ServiceTargetProviderCapability CapabilityType = "service-target-provider" + // Framework service providers enable extensions to provide custom language frameworks and build systems + FrameworkServiceProviderCapability CapabilityType = "framework-service-provider" ) type ProviderType string diff --git a/cli/azd/pkg/project/framework_service.go b/cli/azd/pkg/project/framework_service.go index 2eff279f725..1cb5580df91 100644 --- a/cli/azd/pkg/project/framework_service.go +++ b/cli/azd/pkg/project/framework_service.go @@ -48,7 +48,9 @@ func parseServiceLanguage(kind ServiceLanguageKind) (ServiceLanguageKind, error) return kind, nil } - return ServiceLanguageKind("Unsupported"), fmt.Errorf("unsupported language '%s'", kind) + // Allow unknown languages during parsing - they will be validated later by serviceManager.GetFrameworkService() + // This enables framework service extensions to provide custom languages not built into azd core + return kind, nil } type FrameworkRequirements struct { diff --git a/cli/azd/pkg/project/framework_service_external.go b/cli/azd/pkg/project/framework_service_external.go new file mode 100644 index 00000000000..a502180afa8 --- /dev/null +++ b/cli/azd/pkg/project/framework_service_external.go @@ -0,0 +1,531 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package project + +import ( + "context" + "errors" + "fmt" + "log" + "sync" + + "github.com/azure/azure-dev/cli/azd/pkg/async" + "github.com/azure/azure-dev/cli/azd/pkg/azdext" + "github.com/azure/azure-dev/cli/azd/pkg/extensions" + "github.com/azure/azure-dev/cli/azd/pkg/input" + "github.com/azure/azure-dev/cli/azd/pkg/tools" + "github.com/google/uuid" +) + +// externalTool implements the tools.ExternalTool interface for extension-provided tools +type externalTool struct { + name string + installUrl string +} + +func (et *externalTool) CheckInstalled(ctx context.Context) error { + // Extension-provided tools are assumed to be checked by the extension itself + return nil +} + +func (et *externalTool) InstallUrl() string { + return et.installUrl +} + +func (et *externalTool) Name() string { + return et.name +} + +type ExternalFrameworkService struct { + extension *extensions.Extension + languageName string + languageKind ServiceLanguageKind + console input.Console + + stream azdext.FrameworkService_StreamServer + responseChans sync.Map +} + +// NewExternalFrameworkService creates a new external framework service +func NewExternalFrameworkService( + name string, + kind ServiceLanguageKind, + extension *extensions.Extension, + stream azdext.FrameworkService_StreamServer, + console input.Console, +) FrameworkService { + service := &ExternalFrameworkService{ + extension: extension, + languageName: name, + languageKind: kind, + console: console, + stream: stream, + } + + service.startResponseDispatcher() + + return service +} + +// RequiredExternalTools gets a list of the required external tools for the framework service +func (efs *ExternalFrameworkService) RequiredExternalTools( + ctx context.Context, + serviceConfig *ServiceConfig, +) []tools.ExternalTool { + // Convert serviceConfig to gRPC proto + cleanup := efs.wireConsole() + defer cleanup() + + protoServiceConfig, err := efs.toProtoServiceConfig(serviceConfig) + if err != nil { + return nil + } + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_RequiredExternalToolsRequest{ + RequiredExternalToolsRequest: &azdext.FrameworkServiceRequiredExternalToolsRequest{ + ServiceConfig: protoServiceConfig, + }, + }, + } + + resp, err := efs.sendAndWait(ctx, req, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetRequiredExternalToolsResponse() != nil + }) + if err != nil { + return nil + } + + toolsResp := resp.GetRequiredExternalToolsResponse() + if toolsResp == nil { + return nil + } + + var externalTools []tools.ExternalTool + for _, protoTool := range toolsResp.Tools { + // Create a simple implementation of ExternalTool interface + tool := &externalTool{ + name: protoTool.Name, + installUrl: protoTool.InstallUrl, + } + externalTools = append(externalTools, tool) + } + + return externalTools +} + +// Initialize initializes the framework service for the specified service configuration +func (efs *ExternalFrameworkService) Initialize(ctx context.Context, serviceConfig *ServiceConfig) error { + cleanup := efs.wireConsole() + defer cleanup() + + if serviceConfig == nil { + return errors.New("service configuration is required") + } + + protoServiceConfig, err := efs.toProtoServiceConfig(serviceConfig) + if err != nil { + return err + } + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_InitializeRequest{ + InitializeRequest: &azdext.FrameworkServiceInitializeRequest{ + ServiceConfig: protoServiceConfig, + }, + }, + } + + _, err = efs.sendAndWait(ctx, req, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetInitializeResponse() != nil + }) + return err +} + +// Requirements gets the requirements for the language or framework service +func (efs *ExternalFrameworkService) Requirements() FrameworkRequirements { + ctx := context.Background() + cleanup := efs.wireConsole() + defer cleanup() + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_RequirementsRequest{ + RequirementsRequest: &azdext.FrameworkServiceRequirementsRequest{ + // Empty - requirements are static for a framework + }, + }, + } + + resp, err := efs.sendAndWait(ctx, req, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetRequirementsResponse() != nil + }) + if err != nil { + // Return default requirements on error + return FrameworkRequirements{ + Package: FrameworkPackageRequirements{ + RequireRestore: false, + RequireBuild: false, + }, + } + } + + reqResp := resp.GetRequirementsResponse() + if reqResp == nil || reqResp.Requirements == nil { + return FrameworkRequirements{ + Package: FrameworkPackageRequirements{ + RequireRestore: false, + RequireBuild: false, + }, + } + } + + protoReqs := reqResp.Requirements + requirements := FrameworkRequirements{} + + if protoReqs.Package != nil { + requirements.Package = FrameworkPackageRequirements{ + RequireRestore: protoReqs.Package.RequireRestore, + RequireBuild: protoReqs.Package.RequireBuild, + } + } + + return requirements +} + +// Restore restores dependencies for the framework service +func (efs *ExternalFrameworkService) Restore( + ctx context.Context, + serviceConfig *ServiceConfig, + progress *async.Progress[ServiceProgress], +) (*ServiceRestoreResult, error) { + cleanup := efs.wireConsole() + defer cleanup() + + protoServiceConfig, err := efs.toProtoServiceConfig(serviceConfig) + if err != nil { + return nil, err + } + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_RestoreRequest{ + RestoreRequest: &azdext.FrameworkServiceRestoreRequest{ + ServiceConfig: protoServiceConfig, + }, + }, + } + + resp, err := efs.sendAndWaitWithProgress(ctx, req, progress, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetRestoreResponse() != nil + }) + if err != nil { + return nil, err + } + + restoreResp := resp.GetRestoreResponse() + if restoreResp == nil { + return nil, fmt.Errorf("received empty restore response") + } + + return efs.fromProtoServiceRestoreResult(restoreResp.RestoreResult), nil +} + +// Build builds the source for the framework service +func (efs *ExternalFrameworkService) Build( + ctx context.Context, + serviceConfig *ServiceConfig, + restoreOutput *ServiceRestoreResult, + progress *async.Progress[ServiceProgress], +) (*ServiceBuildResult, error) { + cleanup := efs.wireConsole() + defer cleanup() + + protoServiceConfig, err := efs.toProtoServiceConfig(serviceConfig) + if err != nil { + return nil, err + } + + protoRestoreOutput := efs.toProtoServiceRestoreResult(restoreOutput) + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_BuildRequest{ + BuildRequest: &azdext.FrameworkServiceBuildRequest{ + ServiceConfig: protoServiceConfig, + RestoreOutput: protoRestoreOutput, + }, + }, + } + + resp, err := efs.sendAndWaitWithProgress(ctx, req, progress, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetBuildResponse() != nil + }) + if err != nil { + return nil, err + } + + buildResp := resp.GetBuildResponse() + if buildResp == nil { + return nil, fmt.Errorf("received empty build response") + } + + return efs.fromProtoServiceBuildResult(buildResp.BuildResult), nil +} + +// Package packages the source suitable for deployment +func (efs *ExternalFrameworkService) Package( + ctx context.Context, + serviceConfig *ServiceConfig, + buildOutput *ServiceBuildResult, + progress *async.Progress[ServiceProgress], +) (*ServicePackageResult, error) { + cleanup := efs.wireConsole() + defer cleanup() + + protoServiceConfig, err := efs.toProtoServiceConfig(serviceConfig) + if err != nil { + return nil, err + } + + protoBuildOutput := efs.toProtoServiceBuildResult(buildOutput) + + req := &azdext.FrameworkServiceMessage{ + RequestId: uuid.NewString(), + MessageType: &azdext.FrameworkServiceMessage_PackageRequest{ + PackageRequest: &azdext.FrameworkServicePackageRequest{ + ServiceConfig: protoServiceConfig, + BuildOutput: protoBuildOutput, + }, + }, + } + + resp, err := efs.sendAndWaitWithProgress(ctx, req, progress, func(r *azdext.FrameworkServiceMessage) bool { + return r.GetPackageResponse() != nil + }) + if err != nil { + return nil, err + } + + packageResp := resp.GetPackageResponse() + if packageResp == nil { + return nil, fmt.Errorf("received empty package response") + } + + return fromProtoServicePackageResult(packageResp.PackageResult, nil), nil +} + +// Private methods for gRPC communication + +// helper to send a request and wait for the matching response using async dispatcher +func (efs *ExternalFrameworkService) sendAndWait( + ctx context.Context, + req *azdext.FrameworkServiceMessage, + match func(*azdext.FrameworkServiceMessage) bool, +) (*azdext.FrameworkServiceMessage, error) { + // Create a response channel for this request + respChan := make(chan *azdext.FrameworkServiceMessage, 1) + efs.responseChans.Store(req.RequestId, respChan) + defer efs.responseChans.Delete(req.RequestId) + + // Send the request + if err := efs.stream.Send(req); err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + // Wait for response via the async dispatcher + select { + case <-ctx.Done(): + return nil, ctx.Err() + case resp, ok := <-respChan: + if !ok { + return nil, fmt.Errorf("response channel closed") + } + + if resp.Error != nil { + return nil, fmt.Errorf("framework service error: %s", resp.Error.Message) + } + + if !match(resp) { + return nil, fmt.Errorf("received unexpected response type") + } + + return resp, nil + } +} + +// helper to send a request, handle progress updates, and wait for the matching response +func (efs *ExternalFrameworkService) sendAndWaitWithProgress( + ctx context.Context, + req *azdext.FrameworkServiceMessage, + progress *async.Progress[ServiceProgress], + match func(*azdext.FrameworkServiceMessage) bool, +) (*azdext.FrameworkServiceMessage, error) { + respChan := make(chan *azdext.FrameworkServiceMessage, 1) + efs.responseChans.Store(req.RequestId, respChan) + defer efs.responseChans.Delete(req.RequestId) + + if err := efs.stream.Send(req); err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case resp := <-respChan: + if resp == nil { + return nil, fmt.Errorf("stream closed") + } + + if resp.Error != nil { + return nil, fmt.Errorf("framework service error: %s", resp.Error.Message) + } + + if progressMsg := resp.GetProgressMessage(); progressMsg != nil { + if progress != nil { + progress.SetProgress(ServiceProgress{ + Message: progressMsg.Message, + }) + } + continue // Wait for the actual response + } + + if !match(resp) { + return nil, fmt.Errorf("received unexpected response type") + } + + return resp, nil + } + } +} + +// goroutine to receive and dispatch responses +func (efs *ExternalFrameworkService) startResponseDispatcher() { + go func() { + for { + resp, err := efs.stream.Recv() + if err != nil { + // propagate error to all waiting calls + efs.responseChans.Range(func(key, value any) bool { + ch := value.(chan *azdext.FrameworkServiceMessage) + close(ch) + return true + }) + return + } + if ch, ok := efs.responseChans.Load(resp.RequestId); ok { + + ch.(chan *azdext.FrameworkServiceMessage) <- resp + } else { + log.Printf("No response channel found for RequestId: %s", resp.RequestId) + } + } + }() +} + +func (efs *ExternalFrameworkService) wireConsole() func() { + stdOut := efs.extension.StdOut() + stdErr := efs.extension.StdErr() + stdOut.AddWriter(efs.console.Handles().Stdout) + stdErr.AddWriter(efs.console.Handles().Stderr) + + return func() { + stdOut.RemoveWriter(efs.console.Handles().Stdout) + stdErr.RemoveWriter(efs.console.Handles().Stderr) + } +} + +// Convert ServiceConfig to proto message +func (efs *ExternalFrameworkService) toProtoServiceConfig(serviceConfig *ServiceConfig) (*azdext.ServiceConfig, error) { + if serviceConfig == nil { + return nil, nil + } + + image, err := serviceConfig.Image.Envsubst(func(name string) string { + return "" // Use empty resolver for now, similar to service_target_external.go + }) + if err != nil { + return nil, fmt.Errorf("failed to resolve image value: %w", err) + } + + return &azdext.ServiceConfig{ + Name: serviceConfig.Name, + RelativePath: serviceConfig.RelativePath, + Host: string(serviceConfig.Host), + Language: string(serviceConfig.Language), + OutputPath: serviceConfig.OutputPath, + Image: image, + }, nil +} + +// Convert proto ServiceRestoreResult to local type +func (efs *ExternalFrameworkService) fromProtoServiceRestoreResult( + protoResult *azdext.ServiceRestoreResult, +) *ServiceRestoreResult { + if protoResult == nil { + return nil + } + + result := &ServiceRestoreResult{} + if len(protoResult.Details) > 0 { + result.Details = stringMapToDetailsInterface(protoResult.Details) + } + + return result +} + +// Convert ServiceRestoreResult to proto message +func (efs *ExternalFrameworkService) toProtoServiceRestoreResult(result *ServiceRestoreResult) *azdext.ServiceRestoreResult { + if result == nil { + return nil + } + + details := detailsInterfaceToStringMap(result.Details) + protoResult := &azdext.ServiceRestoreResult{} + if len(details) > 0 { + protoResult.Details = details + } + + return protoResult +} + +// Convert proto ServiceBuildResult to local type +func (efs *ExternalFrameworkService) fromProtoServiceBuildResult( + protoResult *azdext.ServiceBuildResult, +) *ServiceBuildResult { + if protoResult == nil { + return nil + } + + result := &ServiceBuildResult{ + Restore: efs.fromProtoServiceRestoreResult(protoResult.Restore), + } + + if len(protoResult.Details) > 0 { + result.Details = stringMapToDetailsInterface(protoResult.Details) + } + + return result +} + +// Convert ServiceBuildResult to proto message +func (efs *ExternalFrameworkService) toProtoServiceBuildResult(result *ServiceBuildResult) *azdext.ServiceBuildResult { + if result == nil { + return nil + } + + details := detailsInterfaceToStringMap(result.Details) + protoResult := &azdext.ServiceBuildResult{ + Restore: efs.toProtoServiceRestoreResult(result.Restore), + } + + if len(details) > 0 { + protoResult.Details = details + } + + return protoResult +} diff --git a/cli/azd/pkg/project/service_manager.go b/cli/azd/pkg/project/service_manager.go index 05b0b6171c7..fa28db7b717 100644 --- a/cli/azd/pkg/project/service_manager.go +++ b/cli/azd/pkg/project/service_manager.go @@ -191,6 +191,8 @@ func (sm *serviceManager) Initialize(ctx context.Context, serviceConfig *Service } sm.initialized[serviceConfig][frameworkService] = true + } else { + log.Printf("frameworkService already initialized for service: %s", serviceConfig.Name) } if ok := sm.isComponentInitialized(serviceConfig, serviceTarget); !ok { @@ -581,13 +583,26 @@ func (sm *serviceManager) GetFrameworkService(ctx context.Context, serviceConfig serviceConfig.Language = ServiceLanguageDocker } + log.Printf("Attempting to resolve language '%s' for service '%s'", serviceConfig.Language, serviceConfig.Name) if err := sm.serviceLocator.ResolveNamed(string(serviceConfig.Language), &frameworkService); err != nil { - return nil, fmt.Errorf( - "failed to resolve language '%s' for service '%s', %w", - serviceConfig.Language, - serviceConfig.Name, - err, - ) + log.Printf("Failed to resolve language '%s' from IoC container: %v", serviceConfig.Language, err) + // Try to resolve as external framework service from extensions + if errors.Is(err, ioc.ErrResolveInstance) { + // External framework services are not yet implemented + return nil, fmt.Errorf( + "language '%s' is not supported by built-in framework services and no extensions are currently providing it", + serviceConfig.Language, + ) + } else { + return nil, fmt.Errorf( + "failed to resolve language '%s' for service '%s', %w", + serviceConfig.Language, + serviceConfig.Name, + err, + ) + } + } else { + log.Printf("Successfully resolved language '%s' for service '%s'", serviceConfig.Language, serviceConfig.Name) } var compositeFramework CompositeFrameworkService