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

Port Python buildpack output.sh #323

Merged
merged 7 commits into from
Feb 10, 2025
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
97 changes: 54 additions & 43 deletions bin/java
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,49 @@ if [ -f "${JVM_COMMON_DIR}/lib/jvm.sh" ]; then
source "${JVM_COMMON_DIR}/lib/jvm.sh"
fi

# shellcheck source=lib/output.sh
source "${JVM_COMMON_DIR}/lib/output.sh"

install_java_with_overlay() {
local buildDir="${1}"
local cacheDir="${2:-$(mktemp -d)}"
if [ ! -f "${buildDir}/.jdk/bin/java" ]; then
if [ -z "$(_get_system_property "${buildDir}/system.properties" "java.runtime.version")" ]; then
if [ "${STACK}" == "heroku-24" ]; then
warning "No OpenJDK version specified
Your application does not explicitly specify an OpenJDK
version. The latest long-term support (LTS) version will be
installed. This currently is OpenJDK ${DEFAULT_JDK_VERSION}.
output::warning <<-EOF
WARNING: No OpenJDK version specified

Your application does not explicitly specify an OpenJDK
version. The latest long-term support (LTS) version will be
installed. This currently is OpenJDK ${DEFAULT_JDK_VERSION}.

This default version will change when a new LTS version is
released. Your application might fail to build with the new
version. We recommend explicitly setting the required OpenJDK
version for your application.
This default version will change when a new LTS version is
released. Your application might fail to build with the new
version. We recommend explicitly setting the required OpenJDK
version for your application.

To set the OpenJDK version, add or edit the system.properties
file in the root directory of your application to contain:
To set the OpenJDK version, add or edit the system.properties
file in the root directory of your application to contain:

java.runtime.version = ${DEFAULT_JDK_VERSION}
"
java.runtime.version = ${DEFAULT_JDK_VERSION}
EOF
else
warning "No OpenJDK version specified
Your application does not explicitly specify an OpenJDK
version. OpenJDK ${DEFAULT_JDK_VERSION} will be installed.
output::warning <<-EOF
WARNING: No OpenJDK version specified

This default version will change at some point. Your
application might fail to build with the new version. We
recommend explicitly setting the required OpenJDK version for
your application.
Your application does not explicitly specify an OpenJDK
version. OpenJDK ${DEFAULT_JDK_VERSION} will be installed.

To set the OpenJDK version, add or edit the system.properties
file in the root directory of your application to contain:
This default version will change at some point. Your
application might fail to build with the new version. We
recommend explicitly setting the required OpenJDK version for
your application.

java.runtime.version = ${DEFAULT_JDK_VERSION}
"
To set the OpenJDK version, add or edit the system.properties
file in the root directory of your application to contain:

java.runtime.version = ${DEFAULT_JDK_VERSION}
EOF
fi
fi

Expand All @@ -57,24 +64,23 @@ java.runtime.version = ${DEFAULT_JDK_VERSION}

_jvm_mcount "version.${jdkVersion}"
if [[ "$jdkVersion" == *openjdk* ]]; then
status_pending "Installing Heroku OpenJDK $(_get_openjdk_version "${jdkVersion}")"
output::step "Installing Heroku OpenJDK $(_get_openjdk_version "${jdkVersion}")"
_jvm_mcount "vendor.openjdk"
elif [[ "$jdkVersion" == *heroku* ]]; then
status_pending "Installing Heroku OpenJDK $(_get_heroku_version "${jdkVersion}")"
output::step "Installing Heroku OpenJDK $(_get_heroku_version "${jdkVersion}")"
_jvm_mcount "vendor.openjdk"
elif [[ "$jdkVersion" == *zulu* ]]; then
status_pending "Installing Azul Zulu OpenJDK $(_get_zulu_version "${jdkVersion}")"
output::step "Installing Azul Zulu OpenJDK $(_get_zulu_version "${jdkVersion}")"
_jvm_mcount "vendor.zulu"
else
status_pending "Installing OpenJDK ${jdkVersion}"
output::step "Installing OpenJDK ${jdkVersion}"
_jvm_mcount "vendor.default"
fi
install_java "${buildDir}" "${jdkVersion}" "${jdkUrl}"
install_jdk_overlay "${buildDir}/.jdk" "${buildDir}"
_cache_version "${jdkVersion}" "${cacheDir}"
status_done
else
status "Using provided JDK"
output::step "Using provided JDK"
_jvm_mcount "vendor.provided"
fi
}
Expand All @@ -96,7 +102,10 @@ install_java() {
install_certs "${jdkDir}"
echo "${jdkVersion}" >"${jdkDir}/version"
if [ ! -f "${javaExe}" ]; then
error_return "Unable to retrieve the JDK."
output::error <<-EOF
Unable to retrieve the JDK.
EOF

return 1
fi
fi
Expand All @@ -115,19 +124,21 @@ validate_jdk_url() {
local jdkUrl=${1}
local jdkVersion=${2}
if [ "$(_get_url_status "${jdkUrl}")" != "200" ]; then
echo ""
error_return "Unsupported Java version: $jdkVersion

Please check your system.properties file to ensure the java.runtime.version
is among the list of supported version on the Dev Center:
https://devcenter.heroku.com/articles/java-support#supported-java-versions
You can also remove the system.properties from your repo to install
the default ${DEFAULT_JDK_VERSION} version.
If you continue to have trouble, you can open a support ticket here:
https://help.heroku.com

Thanks,
Heroku"
output::error <<-EOF
ERROR: Unsupported Java version: ${jdkVersion}

Please check your system.properties file to ensure the java.runtime.version
is among the list of supported version on the Dev Center:
https://devcenter.heroku.com/articles/java-support#supported-java-versions
You can also remove the system.properties from your repo to install
the default ${DEFAULT_JDK_VERSION} version.
If you continue to have trouble, you can open a support ticket here:
https://help.heroku.com

Thanks,
Heroku
EOF

return 1
fi
}
Expand Down
3 changes: 3 additions & 0 deletions bin/util
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env bash

# While these logging/output functions seem unused, they are used by downstream buildpacks that source this file.
# Do not delete them before making sure that all known downstream buildpacks do not use these anymore.

error() {
echo
echo " ! ERROR: $*" | indent no_first_line_indent
Expand Down
83 changes: 83 additions & 0 deletions lib/output.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash

ANSI_BLUE='\033[1;34m'
ANSI_RED='\033[1;31m'
ANSI_YELLOW='\033[1;33m'
ANSI_RESET='\033[0m'

# Output a single line step message to stdout.
#
# Usage:
# ```
# output::step "Installing Python ..."
# ```
function output::step() {
echo "-----> ${1}"
}

# Indent passed stdout. Typically used to indent command output within a step.
#
# Usage:
# ```
# pip install ... | output::indent
# ```
function output::indent() {
sed --unbuffered "s/^/ /"
}

# Output a styled multi-line notice message to stderr.
#
# Usage:
# ```
# output::notice <<-EOF
# Note: The note summary.
#
# Detailed description.
# EOF
# ```
function output::notice() {
local line
echo >&2
while IFS= read -r line; do
echo -e "${ANSI_BLUE} ! ${line}${ANSI_RESET}" >&2
done
echo >&2
}

# Output a styled multi-line warning message to stderr.
#
# Usage:
# ```
# output::warning <<-EOF
# Warning: The warning summary.
#
# Detailed description.
# EOF
# ```
function output::warning() {
local line
echo >&2
while IFS= read -r line; do
echo -e "${ANSI_YELLOW} ! ${line}${ANSI_RESET}" >&2
done
echo >&2
}

# Output a styled multi-line error message to stderr.
#
# Usage:
# ```
# output::error <<-EOF
# Error: The error summary.
#
# Detailed description.
# EOF
# ```
function output::error() {
local line
echo >&2
while IFS= read -r line; do
echo -e "${ANSI_RED} ! ${line}${ANSI_RESET}" >&2
done
echo >&2
}
95 changes: 95 additions & 0 deletions test/spec/java_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,99 @@
end
end
end

context 'when no OpenJDK version is specified on Heroku-24', stacks: %w[heroku-24] do
let(:app) { Hatchet::Runner.new('empty') }

it 'emits the correct warning' do
app.deploy do
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> JVM Common app detected
remote:
remote: ! WARNING: No OpenJDK version specified
remote: !
remote: ! Your application does not explicitly specify an OpenJDK
remote: ! version. The latest long-term support (LTS) version will be
remote: ! installed. This currently is OpenJDK 21.
remote: !
remote: ! This default version will change when a new LTS version is
remote: ! released. Your application might fail to build with the new
remote: ! version. We recommend explicitly setting the required OpenJDK
remote: ! version for your application.
remote: !
remote: ! To set the OpenJDK version, add or edit the system.properties
remote: ! file in the root directory of your application to contain:
remote: !
remote: ! java.runtime.version = 21
remote:
remote: -----> Installing OpenJDK 21
remote: -----> Discovering process types
remote: Procfile declares types -> (none)
OUTPUT
end
end
end

context 'when no OpenJDK version is specified on Heroku-20/Heroku-22', stacks: %w[heroku-20 heroku-22] do
let(:app) { Hatchet::Runner.new('empty') }

it 'emits the correct warning' do
app.deploy do
expect(clean_output(app.output)).to include(<<~OUTPUT)
remote: -----> JVM Common app detected
remote:
remote: ! WARNING: No OpenJDK version specified
remote: !
remote: ! Your application does not explicitly specify an OpenJDK
remote: ! version. OpenJDK 1.8 will be installed.
remote: !
remote: ! This default version will change at some point. Your
remote: ! application might fail to build with the new version. We
remote: ! recommend explicitly setting the required OpenJDK version for
remote: ! your application.
remote: !
remote: ! To set the OpenJDK version, add or edit the system.properties
remote: ! file in the root directory of your application to contain:
remote: !
remote: ! java.runtime.version = 1.8
remote:
remote: -----> Installing OpenJDK 1.8
remote: -----> Discovering process types
remote: Procfile declares types -> (none)
OUTPUT
end
end
end

context 'when an invalid OpenJDK version has been specified' do
let(:app) { Hatchet::Runner.new('empty', allow_failure: true) }

it 'fails the build with the correct error' do
app.before_deploy do
set_java_version(Dir.pwd, '.NET')
end

app.deploy do
expect(clean_output(app.output)).to match(Regexp.new(<<~REGEX))
remote: -----> JVM Common app detected
remote: -----> Installing OpenJDK .NET
remote:
remote: ! ERROR: Unsupported Java version: .NET
remote: !
remote: ! Please check your system.properties file to ensure the java.runtime.version
remote: ! is among the list of supported version on the Dev Center:
remote: ! https://devcenter.heroku.com/articles/java-support#supported-java-versions
remote: ! You can also remove the system.properties from your repo to install
remote: ! the default .+ version.
remote: ! If you continue to have trouble, you can open a support ticket here:
remote: ! https://help.heroku.com
remote: !
remote: ! Thanks,
remote: ! Heroku
remote:
remote: ! Push rejected, failed to compile JVM Common app.
REGEX
end
end
end
end
4 changes: 2 additions & 2 deletions test/spec/overlay_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
app.deploy do
expect(app.output).to match(Regexp.new(<<~REGEX, Regexp::MULTILINE))
remote: -----> JVM Common app detected
remote: -----> Installing OpenJDK 8... done
remote: -----> Installing OpenJDK 8
REGEX

expect(app.run('cat .jdk/extra.txt')).to eq("extra.txt contents\n")
Expand All @@ -23,7 +23,7 @@
app.deploy do
expect(app.output).to match(Regexp.new(<<~REGEX, Regexp::MULTILINE))
remote: -----> JVM Common app detected
remote: -----> Installing OpenJDK 21... done
remote: -----> Installing OpenJDK 21
REGEX

expect(app.run('cat .jdk/extra.txt')).to eq("extra.txt contents\n")
Expand Down
9 changes: 9 additions & 0 deletions test/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ def write_sys_props(directory, props)
`git add system.properties && git commit -m "setting jdk version"`
end
end

def clean_output(output)
output
# Remove trailing whitespace characters added by Git:
# https://github.com/heroku/hatchet/issues/162
.gsub(/ {8}(?=\R)/, '')
# Remove ANSI colour codes used in buildpack output (e.g. error messages).
.gsub(/\e\[[0-9;]+m/, '')
end