Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement(jib): allow using custom mvnd binaries #4695

Merged
merged 4 commits into from
Jun 23, 2023
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
24 changes: 24 additions & 0 deletions docs/reference/action-types/Build/jib-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ spec:
# Defines the Maven phases to be executed during the Garden build step.
mavenPhases:

# Defines the location of the custom executable Maven Daemon binary.
#
# If not provided, then Maven Daemon 0.9.0 will be downloaded and used.
#
# **Note!** Either `jdkVersion` or `jdkPath` will be used to define `JAVA_HOME` environment variable for the custom
# Maven Daemon.
# To ensure a system JDK usage, please set `jdkPath` to `${local.env.JAVA_HOME}`.
mavendPath:

# Specify extra flags to pass to maven/gradle when building the container image.
extraFlags:
```
Expand Down Expand Up @@ -706,6 +715,21 @@ Defines the Maven phases to be executed during the Garden build step.
| --------------- | ------------- | -------- |
| `array[string]` | `["compile"]` | No |

### `spec.mavendPath`

[spec](#spec) > mavendPath

Defines the location of the custom executable Maven Daemon binary.

If not provided, then Maven Daemon 0.9.0 will be downloaded and used.

**Note!** Either `jdkVersion` or `jdkPath` will be used to define `JAVA_HOME` environment variable for the custom Maven Daemon.
To ensure a system JDK usage, please set `jdkPath` to `${local.env.JAVA_HOME}`.

| Type | Required |
| -------- | -------- |
| `string` | No |

### `spec.extraFlags[]`

[spec](#spec) > extraFlags
Expand Down
24 changes: 24 additions & 0 deletions docs/reference/module-types/jib-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ build:
# Defines the Maven phases to be executed during the Garden build step.
mavenPhases:

# Defines the location of the custom executable Maven Daemon binary.
#
# If not provided, then Maven Daemon 0.9.0 will be downloaded and used.
#
# **Note!** Either `jdkVersion` or `jdkPath` will be used to define `JAVA_HOME` environment variable for the custom
# Maven Daemon.
# To ensure a system JDK usage, please set `jdkPath` to `${local.env.JAVA_HOME}`.
mavendPath:

# Specify extra flags to pass to maven/gradle when building the container image.
extraFlags:

Expand Down Expand Up @@ -1002,6 +1011,21 @@ Defines the Maven phases to be executed during the Garden build step.
| --------------- | ------------- | -------- |
| `array[string]` | `["compile"]` | No |

### `build.mavendPath`

[build](#build) > mavendPath

Defines the location of the custom executable Maven Daemon binary.

If not provided, then Maven Daemon 0.9.0 will be downloaded and used.

**Note!** Either `jdkVersion` or `jdkPath` will be used to define `JAVA_HOME` environment variable for the custom Maven Daemon.
To ensure a system JDK usage, please set `jdkPath` to `${local.env.JAVA_HOME}`.

| Type | Required |
| -------- | -------- |
| `string` | No |

### `build.extraFlags[]`

[build](#build) > extraFlags
Expand Down
97 changes: 97 additions & 0 deletions plugins/jib/build-tool-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2018-2023 Garden Technologies, Inc. <[email protected]>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { Log, PluginContext } from "@garden-io/sdk/types"
import { Writable } from "node:stream"
import execa from "execa"
import { RuntimeError } from "@garden-io/core/build/src/exceptions"

export interface CheckVersionParams {
binaryPath: string
toolName: string
configFieldName: string
}

export function baseErrorMessage({ binaryPath, configFieldName }: CheckVersionParams): string {
return `Gradle binary path "${binaryPath}" is incorrect! Please check the \`${configFieldName}\` configuration option.`
}

export async function getBuildToolVersion(params: CheckVersionParams) {
const { binaryPath, toolName } = params
try {
const res = await execa(binaryPath, ["--version"])
return res.stdout
} catch (error) {
const composeErrorMessage = (err: any): string => {
if (err.code === "EACCES") {
return `${baseErrorMessage(
params
)} It looks like the ${toolName} path defined in the config is not an executable binary.`
} else if (err.code === "ENOENT") {
return `${baseErrorMessage(params)} The ${toolName} path defined in the configuration does not exist.`
} else {
return baseErrorMessage(params)
}
}
throw new RuntimeError(composeErrorMessage(error), { binaryPath })
}
}

export interface VerifyBinaryParams extends CheckVersionParams {
outputVerificationString: string
}

export async function verifyBinaryPath(params: VerifyBinaryParams) {
const { binaryPath, toolName, outputVerificationString } = params
const versionOutput = await getBuildToolVersion(params)
const isMaven = versionOutput.toLowerCase().includes(outputVerificationString)
if (!isMaven) {
throw new RuntimeError(
`${baseErrorMessage(params)} It looks like the ${toolName} path points to a wrong executable binary.`,
{ binaryPath }
)
}
}

export interface BuildToolParams {
ctx: PluginContext
args: string[]
cwd: string
log: Log
openJdkPath: string
binaryPath?: string
outputStream?: Writable
}

export function runBuildTool({
binaryPath,
args,
cwd,
openJdkPath,
outputStream,
}: {
binaryPath: string
args: string[]
cwd: string
openJdkPath: string
outputStream?: Writable
}) {
const res = execa(binaryPath, args, {
cwd,
env: {
JAVA_HOME: openJdkPath,
},
})

if (outputStream) {
res.stdout?.pipe(outputStream)
res.stderr?.pipe(outputStream)
}

return res
}
87 changes: 16 additions & 71 deletions plugins/jib/gradle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import execa from "execa"
import { find } from "lodash"
import { Log, PluginContext, PluginToolSpec } from "@garden-io/sdk/types"
import { PluginError, RuntimeError } from "@garden-io/core/build/src/exceptions"
import { PluginContext, PluginToolSpec } from "@garden-io/sdk/types"
import { PluginError } from "@garden-io/core/build/src/exceptions"
import { resolve } from "path"
import { pathExists } from "fs-extra"
import { Writable } from "stream"
import { runBuildTool, BuildToolParams, verifyBinaryPath, VerifyBinaryParams } from "./build-tool-base"

export const gradleVersion = "7.5.1"

Expand Down Expand Up @@ -68,44 +67,13 @@ export function getGradleTool(ctx: PluginContext) {
return tool
}

const baseErrorMessage = (gradlePath: string): string =>
`Gradle binary path "${gradlePath}" is incorrect! Please check the \`gradlePath\` configuration option.`

async function checkGradleVersion(gradlePath: string) {
try {
const res = await execa(gradlePath, ["--version"])
return res.stdout
} catch (error) {
const composeErrorMessage = (err: any): string => {
if (err.code === "EACCES") {
return `${baseErrorMessage(
gradlePath
)} It looks like the Gradle path defined in the config is not an executable binary.`
} else if (err.code === "ENOENT") {
return `${baseErrorMessage(gradlePath)} The Gradle path defined in the configuration does not exist.`
} else {
return baseErrorMessage(gradlePath)
}
}
throw new RuntimeError(composeErrorMessage(error), { gradlePath })
}
}

let gradlePathValid = false

async function verifyGradlePath(gradlePath: string) {
async function verifyGradlePath(params: VerifyBinaryParams) {
if (gradlePathValid) {
return
}

const versionOutput = await checkGradleVersion(gradlePath)
const isGradle = versionOutput.toLowerCase().includes("gradle")
if (!isGradle) {
throw new RuntimeError(
`${baseErrorMessage(gradlePath)} It looks like the Gradle path points to a non-Gradle executable binary.`,
{ gradlePath }
)
}
await verifyBinaryPath(params)
gradlePathValid = true
}

Expand All @@ -116,29 +84,17 @@ async function verifyGradlePath(gradlePath: string) {
* If no explicit binary specific, then a `./gradlew` script will be used if it's available in the specified directory.
* Otherwise, the Gradle distribution will be downloaded and used.
*/
export async function gradle({
ctx,
args,
cwd,
log,
openJdkPath,
gradlePath,
outputStream,
}: {
ctx: PluginContext
args: string[]
cwd: string
log: Log
openJdkPath: string
gradlePath?: string
outputStream: Writable
}) {
export async function gradle({ ctx, args, cwd, log, openJdkPath, binaryPath, outputStream }: BuildToolParams) {
let effectiveGradlePath: string

if (!!gradlePath) {
log.verbose(`Using explicitly specified Gradle binary from ${gradlePath}`)
effectiveGradlePath = gradlePath
await verifyGradlePath(effectiveGradlePath)
if (!!binaryPath) {
log.verbose(`Using explicitly specified Gradle binary from ${binaryPath}`)
effectiveGradlePath = binaryPath
await verifyGradlePath({
binaryPath,
toolName: "Gradle",
configFieldName: "gradlePath",
outputVerificationString: "gradle",
})
} else {
const gradlewPath = resolve(cwd, process.platform === "win32" ? "gradlew.bat" : "gradlew")
if (await pathExists(gradlewPath)) {
Expand All @@ -156,16 +112,5 @@ export async function gradle({
}

log.debug(`Execing ${effectiveGradlePath} ${args.join(" ")}`)

const res = execa(effectiveGradlePath, args, {
cwd,
env: {
JAVA_HOME: openJdkPath,
},
})

res.stdout?.pipe(outputStream)
res.stderr?.pipe(outputStream)

return res
return runBuildTool({ binaryPath: effectiveGradlePath, args, cwd, openJdkPath, outputStream })
}
19 changes: 14 additions & 5 deletions plugins/jib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { dedent } from "@garden-io/sdk/util/string"

import { openJdkSpecs } from "./openjdk"
import { mavenSpec, mvn, mvnVersion } from "./maven"
import { mavendSpec, mvnd } from "./mavend"
import { mavendSpec, mvnd, mvndVersion } from "./mavend"
import { gradle, gradleSpec, gradleVersion } from "./gradle"

// TODO: gradually get rid of these core dependencies, move some to SDK etc.
Expand Down Expand Up @@ -113,6 +113,14 @@ const jibBuildSchemaKeys = () => ({
.items(joi.string())
.default(["compile"])
.description("Defines the Maven phases to be executed during the Garden build step."),
mavendPath: joi.string().optional().description(dedent`
Defines the location of the custom executable Maven Daemon binary.

If not provided, then Maven Daemon ${mvndVersion} will be downloaded and used.

**Note!** Either \`jdkVersion\` or \`jdkPath\` will be used to define \`JAVA_HOME\` environment variable for the custom Maven Daemon.
To ensure a system JDK usage, please set \`jdkPath\` to \`${systemJdkGardenEnvVar}\`.
`),
extraFlags: joi
.sparseArray()
.items(joi.string())
Expand Down Expand Up @@ -166,8 +174,8 @@ export const gardenPlugin = () =>
handlers: {
build: async (params) => {
const { ctx, log, action } = params
const spec = action.getSpec()
const { jdkVersion, jdkPath, mavenPhases, mavenPath, gradlePath } = spec
const spec = action.getSpec() as JibBuildActionSpec
const { jdkVersion, jdkPath, mavenPhases, mavenPath, mavendPath, gradlePath } = spec

let openJdkPath: string
if (!!jdkPath) {
Expand Down Expand Up @@ -217,7 +225,7 @@ export const gardenPlugin = () =>
cwd: action.basePath(),
args: [...mavenPhases, ...args],
openJdkPath,
mavenPath,
binaryPath: mavenPath,
outputStream,
})
} else if (projectType === "mavend") {
Expand All @@ -227,6 +235,7 @@ export const gardenPlugin = () =>
cwd: action.basePath(),
args: [...mavenPhases, ...args],
openJdkPath,
binaryPath: mavendPath,
outputStream,
})
} else {
Expand All @@ -236,7 +245,7 @@ export const gardenPlugin = () =>
cwd: action.basePath(),
args,
openJdkPath,
gradlePath,
binaryPath: gradlePath,
outputStream,
})
}
Expand Down
Loading