Skip to content
Merged
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
6 changes: 6 additions & 0 deletions cli/azd/.vscode/cspell-azd-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ containerapp
csharpapp
devel
dockerproject
dskip
eastus
envname
errcheck
Expand All @@ -40,11 +41,13 @@ GOARCH
godotenv
golangci
ineffassign
javac
jmes
keychain
ldflags
mgmt
mgutz
mvnw
nobanner
nodeapp
nolint
Expand All @@ -62,6 +65,7 @@ retriable
rzip
semconv
serverfarms
setenvs
sstore
staticcheck
staticwebapp
Expand All @@ -73,6 +77,8 @@ teamcity
tracesdk
tracetest
unmarshalling
unsets
unsetenvs
utsname
westus2
yacspin
106 changes: 106 additions & 0 deletions cli/azd/pkg/project/framework_service_maven.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package project

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

"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/tools"
"github.com/azure/azure-dev/cli/azd/pkg/tools/javac"
"github.com/azure/azure-dev/cli/azd/pkg/tools/maven"
"github.com/otiai10/copy"
)

// The default, conventional App Service Java package name
const AppServiceJavaPackageName = "app.jar"

type mavenProject struct {
config *ServiceConfig
env *environment.Environment
mavenCli maven.MavenCli
javacCli javac.JavacCli
}

func (m *mavenProject) RequiredExternalTools() []tools.ExternalTool {
return []tools.ExternalTool{
m.mavenCli,
m.javacCli,
}
}

func (m *mavenProject) Package(ctx context.Context, progress chan<- string) (string, error) {
publishRoot, err := os.MkdirTemp("", "azd")
if err != nil {
return "", fmt.Errorf("creating staging directory: %w", err)
}

progress <- "Creating deployment package"
if err := m.mavenCli.Package(ctx, m.config.Path()); err != nil {
return "", err
}

publishSource := m.config.Path()

if m.config.OutputPath != "" {
publishSource = filepath.Join(publishSource, m.config.OutputPath)
} else {
publishSource = filepath.Join(publishSource, "target")
}

entries, err := os.ReadDir(publishSource)
if err != nil {
return "", fmt.Errorf("discovering JAR files in %s: %w", publishSource, err)
}

matches := []string{}
for _, entry := range entries {
if entry.IsDir() {
continue
}

if name := entry.Name(); strings.HasSuffix(name, ".jar") {
matches = append(matches, name)
}
}

if len(matches) == 0 {
return "", fmt.Errorf("no JAR files found in %s", publishSource)
}
if len(matches) > 1 {
names := strings.Join(matches, ", ")
return "", fmt.Errorf("multiple JAR files found in %s: %s. Only a single runnable JAR file is expected", publishSource, names)
}

err = copy.Copy(filepath.Join(publishSource, matches[0]), filepath.Join(publishRoot, AppServiceJavaPackageName))
if err != nil {
return "", fmt.Errorf("copying to staging directory failed: %w", err)
}

return publishRoot, nil
}

func (m *mavenProject) InstallDependencies(ctx context.Context) error {
if err := m.mavenCli.ResolveDependencies(ctx, m.config.Path()); err != nil {
return fmt.Errorf("resolving maven dependencies: %w", err)
}

return nil
}

func (m *mavenProject) Initialize(ctx context.Context) error {
return nil
}

func NewMavenProject(ctx context.Context, config *ServiceConfig, env *environment.Environment) FrameworkService {
runner := exec.GetCommandRunner(ctx)
return &mavenProject{
config: config,
env: env,
mavenCli: maven.NewMavenCli(runner, config.Path(), config.Project.Path),
javacCli: javac.NewCli(runner),
}
}
2 changes: 2 additions & 0 deletions cli/azd/pkg/project/service_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ func (sc *ServiceConfig) GetFrameworkService(ctx context.Context, env *environme
frameworkService = NewPythonProject(ctx, sc, env)
case "js", "ts":
frameworkService = NewNpmProject(ctx, sc, env)
case "java":
frameworkService = NewMavenProject(ctx, sc, env)
default:
return nil, fmt.Errorf("unsupported language '%s' for service '%s'", sc.Language, sc.Name)
}
Expand Down
124 changes: 124 additions & 0 deletions cli/azd/pkg/tools/javac/javac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package javac

import (
"context"
"errors"
"fmt"
"os"
osexec "os/exec"
"path/filepath"

"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/tools"
"github.com/blang/semver/v4"
)

const javac = "javac"

type JavacCli interface {
tools.ExternalTool
}

type javacCli struct {
cmdRun exec.CommandRunner
}

func NewCli(cmdRun exec.CommandRunner) JavacCli {
return &javacCli{
cmdRun: cmdRun,
}
}

func (j *javacCli) VersionInfo() tools.VersionInfo {
return tools.VersionInfo{
MinimumVersion: semver.Version{
Major: 17,
Minor: 0,
Patch: 0},
UpdateCommand: "Visit the website for your installed JDK to upgrade",
}
}

func (j *javacCli) CheckInstalled(ctx context.Context) (bool, error) {
path, err := getInstalledPath()
if err != nil {
return false, err
}

runResult, err := j.cmdRun.Run(ctx, exec.RunArgs{
Cmd: path,
Args: []string{"--version"},
})
if err != nil {
return false, fmt.Errorf("checking javac version: %w", err)
}

jdkVer, err := tools.ExtractSemver(runResult.Stdout)
if err != nil {
return false, fmt.Errorf("converting to semver version fails: %w", err)
}

requiredVersion := j.VersionInfo()
if jdkVer.LT(requiredVersion.MinimumVersion) {
return false, &tools.ErrSemver{ToolName: j.Name(), VersionInfo: requiredVersion}
}

return true, nil
}

func (j *javacCli) InstallUrl() string {
return "https://www.microsoft.com/openjdk"
}

func (j *javacCli) Name() string {
return "Java JDK"
}

// getInstalledPath returns the installed javac path.
//
// javac is located by consulting, in search order:
// - JAVA_HOME (if set)
// - PATH
//
// An error is returned if javac could not be found, or if invalid locations are provided.
func getInstalledPath() (string, error) {
path, err := findByEnvVar("JAVA_HOME")
if path != "" {
return path, nil
}
if err != nil {
return "", fmt.Errorf("JAVA_HOME is set to an invalid directory: %w", err)
}

path, err = osexec.LookPath(javac)
if err == nil {
return path, nil
}

if !errors.Is(err, osexec.ErrNotFound) {
return "", fmt.Errorf("failed looking up javac in PATH: %w", err)
}

return "", errors.New(
"javac could not be found. Set JAVA_HOME environment variable to point to your Java JDK installation, " +
"or include javac in your PATH environment variable")
}

// findByEnvVar returns the javac path by the following environment variable home directory.
//
// An error is returned if an error occurred while finding.
// If the environment variable home directory is unset, an empty string is returned with no error.
func findByEnvVar(envVar string) (string, error) {
home := os.Getenv(envVar)
if home == "" {
return "", nil
}

absPath := filepath.Join(home, "bin", javac)
absPath, err := osexec.LookPath(absPath)
if err != nil {
return "", err
}

return absPath, nil
}
Loading