Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cli/azd/cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet"
"github.com/azure/azure-dev/cli/azd/pkg/tools/git"
"github.com/azure/azure-dev/cli/azd/pkg/tools/github"
golangtools "github.com/azure/azure-dev/cli/azd/pkg/tools/golang"
"github.com/azure/azure-dev/cli/azd/pkg/tools/javac"
"github.com/azure/azure-dev/cli/azd/pkg/tools/kubectl"
"github.com/azure/azure-dev/cli/azd/pkg/tools/language"
Expand Down Expand Up @@ -723,6 +724,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
container.MustRegisterSingleton(dotnet.NewCli)
container.MustRegisterSingleton(git.NewCli)
container.MustRegisterSingleton(github.NewGitHubCli)
container.MustRegisterSingleton(golangtools.NewCli)
container.MustRegisterSingleton(javac.NewCli)
container.MustRegisterSingleton(kubectl.NewCli)
container.MustRegisterSingleton(maven.NewCli)
Expand Down Expand Up @@ -809,6 +811,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
project.ServiceLanguageJavaScript: project.NewNodeProject,
project.ServiceLanguageTypeScript: project.NewNodeProject,
project.ServiceLanguageJava: project.NewMavenProject,
project.ServiceLanguageGo: project.NewGoProject,
project.ServiceLanguageDocker: project.NewDockerProject,
project.ServiceLanguageSwa: project.NewSwaProject,
project.ServiceLanguageCustom: project.NewCustomProject,
Expand Down
5 changes: 5 additions & 0 deletions cli/azd/pkg/project/framework_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
ServiceLanguageTypeScript ServiceLanguageKind = "ts"
ServiceLanguagePython ServiceLanguageKind = "python"
ServiceLanguageJava ServiceLanguageKind = "java"
ServiceLanguageGo ServiceLanguageKind = "go"
ServiceLanguageDocker ServiceLanguageKind = "docker"
ServiceLanguageSwa ServiceLanguageKind = "swa"
ServiceLanguageCustom ServiceLanguageKind = "custom"
Expand All @@ -33,6 +34,9 @@ func parseServiceLanguage(kind ServiceLanguageKind) (ServiceLanguageKind, error)
if string(kind) == "py" {
return ServiceLanguagePython, nil
}
if string(kind) == "golang" {
return ServiceLanguageGo, nil
}

switch kind {
case ServiceLanguageNone,
Expand All @@ -43,6 +47,7 @@ func parseServiceLanguage(kind ServiceLanguageKind) (ServiceLanguageKind, error)
ServiceLanguageTypeScript,
ServiceLanguagePython,
ServiceLanguageJava,
ServiceLanguageGo,
ServiceLanguageDocker,
ServiceLanguageCustom:
// Excluding ServiceLanguageSwa since it is implicitly derived currently,
Expand Down
206 changes: 206 additions & 0 deletions cli/azd/pkg/project/framework_service_go.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package project

import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/azure/azure-dev/cli/azd/pkg/async"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/tools"
"github.com/azure/azure-dev/cli/azd/pkg/tools/golang"
"github.com/otiai10/copy"
)

const (
// goBinaryName is the compiled binary name for Azure Functions Go worker on Linux.
goBinaryName = "app"
)

type goProject struct {
env *environment.Environment
goCli *golang.Cli
}

// NewGoProject creates a new instance of a Go project framework service.
func NewGoProject(
env *environment.Environment,
goCli *golang.Cli,
) FrameworkService {
return &goProject{
env: env,
goCli: goCli,
}
}

func (gp *goProject) Requirements() FrameworkRequirements {
return FrameworkRequirements{
Package: FrameworkPackageRequirements{
RequireRestore: false,
RequireBuild: true,
},
}
}

// RequiredExternalTools returns the Go CLI as a required tool.
func (gp *goProject) RequiredExternalTools(
_ context.Context,
_ *ServiceConfig,
) []tools.ExternalTool {
return []tools.ExternalTool{gp.goCli}
}

// Initialize is a no-op for Go projects.
func (gp *goProject) Initialize(
ctx context.Context,
serviceConfig *ServiceConfig,
) error {
return nil
}

// Restore downloads Go module dependencies.
func (gp *goProject) Restore(
ctx context.Context,
serviceConfig *ServiceConfig,
serviceContext *ServiceContext,
progress *async.Progress[ServiceProgress],
) (*ServiceRestoreResult, error) {
progress.SetProgress(NewServiceProgress("Downloading Go modules"))
if err := gp.goCli.ModDownload(ctx, serviceConfig.Path()); err != nil {
return nil, fmt.Errorf("restoring Go dependencies: %w", err)
}

return &ServiceRestoreResult{
Artifacts: ArtifactCollection{
{
Kind: ArtifactKindDirectory,
Location: serviceConfig.Path(),
LocationKind: LocationKindLocal,
Metadata: map[string]string{
"projectPath": serviceConfig.Path(),
"framework": "go",
},
},
},
}, nil
}

// Build compiles the Go project, cross-compiling for linux/amd64.
func (gp *goProject) Build(
ctx context.Context,
serviceConfig *ServiceConfig,
serviceContext *ServiceContext,
progress *async.Progress[ServiceProgress],
) (*ServiceBuildResult, error) {
progress.SetProgress(NewServiceProgress("Compiling Go project"))

buildDir, err := os.MkdirTemp("", "azd-go-build")
if err != nil {
return nil, fmt.Errorf("creating build directory: %w", err)
}

outputPath := filepath.Join(buildDir, goBinaryName)

// Cross-compile for linux/amd64 (Azure Functions target)
buildEnv := append(
gp.env.Environ(),
"GOOS=linux",
"GOARCH=amd64",
"CGO_ENABLED=0",
)

if err := gp.goCli.Build(
ctx, serviceConfig.Path(), outputPath, buildEnv,
); err != nil {
return nil, fmt.Errorf("compiling Go project: %w", err)
}

return &ServiceBuildResult{
Artifacts: ArtifactCollection{
{
Kind: ArtifactKindDirectory,
Location: buildDir,
LocationKind: LocationKindLocal,
Metadata: map[string]string{
"buildPath": buildDir,
"binaryPath": outputPath,
"framework": "go",
"targetOS": "linux",
"targetArch": "amd64",
"buildOS": runtime.GOOS,
"buildArch": runtime.GOARCH,
},
},
},
}, nil
}

// Package stages the compiled binary and host.json into a deployment directory
// suitable for Azure Functions zip deploy.
// On Flex Consumption with runtime 'go', the platform provides the worker.config.json
// and proxy binary — the deployment package only needs the app binary and host.json.
func (gp *goProject) Package(
ctx context.Context,
serviceConfig *ServiceConfig,
serviceContext *ServiceContext,
progress *async.Progress[ServiceProgress],
) (*ServicePackageResult, error) {
progress.SetProgress(NewServiceProgress("Staging Go Functions deployment"))

// Resolve build output directory
buildDir := ""
if artifact, found := serviceContext.Build.FindFirst(
WithKind(ArtifactKindDirectory),
); found {
buildDir = artifact.Location
}
if buildDir == "" {
return nil, fmt.Errorf("no build output found in service context")
}

packageDir, err := os.MkdirTemp("", "azd-go-package")
if err != nil {
return nil, fmt.Errorf("creating package directory: %w", err)
}

// Copy compiled binary (ensure execute permission is preserved for the zip)
progress.SetProgress(NewServiceProgress("Copying compiled binary"))
binaryPath := filepath.Join(buildDir, goBinaryName)
destBinaryPath := filepath.Join(packageDir, goBinaryName)
if err := copy.Copy(binaryPath, destBinaryPath); err != nil {
return nil, fmt.Errorf("copying Go binary: %w", err)
}
if err := os.Chmod(destBinaryPath, 0755); err != nil {
return nil, fmt.Errorf("setting binary permissions: %w", err)
}

// Copy host.json from user project
hostJSONSrc := filepath.Join(serviceConfig.Path(), "host.json")
if _, err := os.Stat(hostJSONSrc); err == nil {
if err := copy.Copy(
hostJSONSrc, filepath.Join(packageDir, "host.json"),
); err != nil {
return nil, fmt.Errorf("copying host.json: %w", err)
}
}

return &ServicePackageResult{
Artifacts: ArtifactCollection{
{
Kind: ArtifactKindDirectory,
Location: packageDir,
LocationKind: LocationKindLocal,
Metadata: map[string]string{
"packagePath": packageDir,
"framework": "go",
"host": "azure-function",
},
},
},
}, nil
}
Loading
Loading