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

Spring boot extension #693

Draft
wants to merge 50 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
276e669
feat: jlink plugin
vpa1977 Aug 28, 2024
1512a53
feat: register jlink plugin
vpa1977 Aug 28, 2024
b7654fa
lint: jlink_plugin.py - reformat
vpa1977 Aug 28, 2024
5d56347
lint: register.py reformat
vpa1977 Aug 28, 2024
c0d8419
lint: test_jlink_plugin.py reformat
vpa1977 Aug 28, 2024
ea176cf
feat: test_jlink_plugin.py integration wip
vpa1977 Aug 28, 2024
545b707
fix: jlink_plugin - dep commands
vpa1977 Aug 28, 2024
3f375f8
feat: rename slice to install dependencies
vpa1977 Aug 29, 2024
2ddf8cf
feat: add dep slices
vpa1977 Aug 29, 2024
4e3615d
test: update test assertions
vpa1977 Aug 29, 2024
ab8598f
test: drop project test.
vpa1977 Aug 29, 2024
7f76cc5
lint: reformat jlink_plugin
vpa1977 Aug 29, 2024
a4844ed
lint: reformat test_jlink_plugin
vpa1977 Aug 29, 2024
7798ac8
test: fix directory.
vpa1977 Aug 29, 2024
c277ac9
Merge branch 'main' into jlink-plugin
vpa1977 Aug 29, 2024
2e18adb
lint(test_jlink_plugin.py): unused import.
vpa1977 Aug 29, 2024
3411fbd
deps: add git and openjdk-21-jdk
vpa1977 Aug 29, 2024
29675ac
Merge branch 'jlink-plugin' of github.com:vpa1977/rockcraft into jlin…
vpa1977 Aug 29, 2024
ceb65d1
lint(jlink_plugin.py): mypy fixes.
vpa1977 Aug 29, 2024
6e875b8
fix(jlink_plugin.py): drop chisel dependency.
vpa1977 Aug 29, 2024
a776464
test(test_jlink_plugin.py): fix assertion
vpa1977 Aug 29, 2024
66ab695
deps: install chisel snap
vpa1977 Aug 29, 2024
212c9fd
deps: install chisel snap
vpa1977 Aug 29, 2024
48b54f4
lint(test_jlink_plugin.py): remove exclusion.
vpa1977 Aug 29, 2024
efde661
test(jlink_plugin): add integration test with jar
vpa1977 Aug 29, 2024
943d333
fix: add minimal default classpath
vpa1977 Aug 29, 2024
e2ce4b5
lint: formatting
vpa1977 Aug 29, 2024
a288da3
lint(test_jlink_plugin.py): remove f-string
vpa1977 Aug 29, 2024
3f2f61a
fix(jlink_plugin): capitalize variables
vpa1977 Aug 29, 2024
dbe14dd
fix(jlink_plugin): use options.jlink_java_version
vpa1977 Aug 29, 2024
d23ed5f
lint(jlink_plugin): fix command formatting
vpa1977 Aug 29, 2024
636e95b
Merge branch 'main' into jlink-plugin
vpa1977 Aug 29, 2024
3ba87e2
fix(jlink_plugin): drop chiselling dependencies
vpa1977 Aug 30, 2024
7a65ec3
Merge branch 'jlink-plugin' of github.com:vpa1977/rockcraft into jlin…
vpa1977 Aug 30, 2024
4c734fc
lint(test_jlink_plugin): reformat.
vpa1977 Aug 30, 2024
579a50d
feat(spring_boot_framework): wip implementation
vpa1977 Aug 29, 2024
71ee60e
feat(spring-boot-framework): add runtime-deps part
vpa1977 Aug 30, 2024
89ec22b
fix(spring-boot-framework): set JAVA_HOME in build
vpa1977 Aug 30, 2024
e3905c2
lint(spring-boot-framework): reformat
vpa1977 Aug 30, 2024
9ad391e
fix(spring-boot-framework): fix build-environment
vpa1977 Aug 30, 2024
7da96ac
fix(spring-boot): path to staging location
vpa1977 Aug 30, 2024
9c510ac
fix: add base-files_base to deps
vpa1977 Aug 30, 2024
f749a2e
fix(spring-boot, maven): only copy jars from target
vpa1977 Sep 2, 2024
44da819
fix(spring-boot, gradle): add stage and prime
vpa1977 Sep 2, 2024
01f1f68
lint(spring-boot): do not use deprecated variable
vpa1977 Sep 2, 2024
c6d725d
fix(spring-boot, gradle): use stage directory
vpa1977 Sep 2, 2024
be667bc
fix: test assertion
vpa1977 Sep 2, 2024
f33557a
lint: drop unused import
vpa1977 Sep 2, 2024
a64b33d
fix: check that part is not overriden
vpa1977 Sep 27, 2024
f8d8016
fix: do not return run-user
vpa1977 Sep 27, 2024
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: 4 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,14 @@ jobs:

- name: Install dependencies
run: |
echo "::group::snap install"
sudo snap install chisel --edge
echo "::endgroup::"
echo "::group::apt-get update"
sudo apt-get update
echo "::endgroup::"
echo "::group::apt-get install..."
sudo apt-get install -y python3 python3-dev libapt-pkg-dev libyaml-dev umoci
sudo apt-get install -y python3 python3-dev libapt-pkg-dev libyaml-dev umoci git openjdk-21-jdk
echo "::endgroup::"
echo "::group::pip install"
python -m pip install -U wheel setuptools pip
Expand All @@ -99,4 +102,3 @@ jobs:
- name: Run integration tests
run: |
make test-integrations

2 changes: 2 additions & 0 deletions rockcraft/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from ._utils import apply_extensions
from .go import GoFramework
from .gunicorn import DjangoFramework, FlaskFramework
from .spring_boot import SpringBootFramework
from .registry import get_extension_class, get_extension_names, register, unregister

__all__ = [
Expand All @@ -32,3 +33,4 @@
register("django-framework", DjangoFramework)
register("flask-framework", FlaskFramework)
register("go-framework", GoFramework)
register("spring-boot-framework", SpringBootFramework)
149 changes: 149 additions & 0 deletions rockcraft/extensions/spring_boot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""An extension for the Java runtime."""

import os
from typing import Tuple
from typing import Any
from typing import Dict
from overrides import override

from ..errors import ExtensionError
from .extension import Extension


class SpringBootFramework(Extension):

def _check_project(self):
"""Ensure that either pom.xml or gradlew is present."""
if "spring-boot-framework/install-app" not in self.yaml_data.get("parts", {}):
if not os.path.exists(f"{self.project_root}/pom.xml") and not os.path.exists(
f"{self.project_root}/gradlew"
):
raise ExtensionError(
"missing pom.xml or gradlew file",
doc_slug="/reference/extensions/spring-boot-framework",
logpath_report=False,
)

@property
def name(self) -> str:
"""Return the normalized name of the rockcraft project."""
return self.yaml_data["name"].replace("-", "_").lower()

@staticmethod
@override
def get_supported_bases() -> Tuple[str, ...]:
"""Return supported bases."""
return "bare", "[email protected]", "ubuntu:24.04"

@staticmethod
@override
def is_experimental(base: str | None) -> bool:
"""Check if the extension is in an experimental state."""
return True

@override
def get_part_snippet(self) -> Dict[str, Any]:
"""Return the part snippet to apply to existing parts."""
return {}

def get_root_snippet(self) -> Dict[str, Any]:
self._check_project()
return {}

def get_runtime_deps_part(self) -> Dict[str, Any]:
if "spring-boot-framework/runtime-deps" not in self.yaml_data.get("parts", {}):
return {
"plugin": "nil",
"source": "https://github.com/vpa1977/chisel-releases",
"source-type": "git",
"source-branch": "24.04-openjdk-21-jre-headless",
"override-build": """
chisel cut --release ./ --root ${CRAFT_PART_INSTALL} \
base-files_base \
libc6_libs \
libgcc-s1_libs \
libstdc++6_libs \
zlib1g_libs \
libnss3_libs
craftctl default
""",
}

def gen_install_app_part(self) -> Dict[str, Any]:
if "spring-boot-framework/install-app" not in self.yaml_data.get("parts", {}):
if os.path.exists(f"{self.project_root}/pom.xml"):
return {
"plugin": "nil",
"source": ".",
"source-type": "local",
"build-packages": ["openjdk-21-jdk", "maven"],
"build-environment": [
{
"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"
}
],
"override-build": """
mvn package
mkdir -p ${CRAFT_PART_INSTALL}/jars
find ${CRAFT_PART_BUILD}/target -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\;
craftctl default
""",
}
elif os.path.exists(f"{self.project_root}/gradlew"):
return {
"plugin": "nil",
"source": ".",
"source-type": "local",
"build-packages": ["openjdk-21-jdk"],
"build-environment": [
{
"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"
}
],
"override-build": "./gradlew jar --no-daemon",
"override-stage": """
mkdir -p ${CRAFT_STAGE}/jars
find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_STAGE}/jars \\;
craftctl default
""",
"override-prime": """
cp -r ${CRAFT_STAGE}/jars ${CRAFT_PRIME}
craftctl default
""",
}
return {}

def get_runtime_app_part(self) -> Dict[str, Any]:
if "spring-boot-framework/runtime" not in self.yaml_data.get("parts", {}):
return {
"plugin": "jlink",
"after": [
"spring-boot-framework/install-app",
"spring-boot-framework/runtime-deps",
],
}
return {}

def get_parts_snippet(self) -> dict[str, Any]:
"""Return the parts to add to parts."""
return {
"spring-boot-framework/install-app": self.gen_install_app_part(),
"spring-boot-framework/runtime": self.get_runtime_app_part(),
"spring-boot-framework/runtime-deps": self.get_runtime_deps_part(),
}
3 changes: 2 additions & 1 deletion rockcraft/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""Rockcraft-specific plugins."""

from .python_plugin import PythonPlugin
from .jlink_plugin import JLinkPlugin
from .register import get_plugins, register

__all__ = ["PythonPlugin", "get_plugins", "register"]
__all__ = ["PythonPlugin", "JLinkPlugin", "get_plugins", "register"]
112 changes: 112 additions & 0 deletions rockcraft/plugins/jlink_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""The Java runtime plugin."""

from typing import Dict, List, Literal, Set, cast

from craft_parts.plugins import Plugin, PluginProperties
from overrides import override


class JLinkPluginProperties(PluginProperties, frozen=True):
"""The part properties used by the JLink plugin."""

plugin: Literal["jlink"] = "jlink"
jlink_java_version: int = 21
jlink_jars: list[str] = []


class JLinkPlugin(Plugin):
"""Create a Java Runtime using JLink."""

properties_class = JLinkPluginProperties

@override
def get_build_packages(self) -> Set[str]:
options = cast(JLinkPluginProperties, self._options)
return {f"openjdk-{options.jlink_java_version}-jdk"}

@override
def get_build_environment(self) -> Dict[str, str]:
return {}

@override
def get_build_snaps(self) -> Set[str]:
return set()

@override
def get_build_commands(self) -> List[str]:
"""Return a list of commands to run during the build step."""
options = cast(JLinkPluginProperties, self._options)

commands = []

if len(options.jlink_jars) > 0:
jars = " ".join(["${CRAFT_STAGE}/" + x for x in options.jlink_jars])
commands.append(f"PROCESS_JARS={jars}")
else:
commands.append("PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)")

# create temp folder
commands.append("mkdir -p ${CRAFT_PART_BUILD}/tmp")
# extract jar files into temp folder
commands.append(
"(cd ${CRAFT_PART_BUILD}/tmp && for jar in ${PROCESS_JARS}; do jar xvf ${jar}; done;)"
)
commands.append("CPATH=$(find ${CRAFT_PART_BUILD}/tmp -type f -name *.jar)")
commands.append("CPATH=$(echo ${CPATH}:. | sed s'/[[:space:]]/:/'g)")
commands.append("echo ${CPATH}")
commands.append(
"""if [ "x${PROCESS_JARS}" != "x" ]; then
deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps \
--print-module-deps --multi-release """
+ str(options.jlink_java_version)
+ """ ${PROCESS_JARS}); else deps=java.base; fi
"""
)
commands.append(
"INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-"
+ str(options.jlink_java_version)
+ "-openjdk-${CRAFT_TARGET_ARCH}/"
)

commands.append(
"rm -rf ${INSTALL_ROOT} && jlink --add-modules ${deps} --output ${INSTALL_ROOT}"
)
# create /usr/bin/java link
commands.append(
"(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-"
+ str(options.jlink_java_version)
+ "-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)"
)
commands.append("mkdir -p ${CRAFT_PART_INSTALL}/etc/ssl/certs/java/")
# link cacerts
commands.append(
"cp /etc/ssl/certs/java/cacerts ${CRAFT_PART_INSTALL}/etc/ssl/certs/java/cacerts"
)
commands.append("cd ${CRAFT_PART_INSTALL}")
commands.append(
"rm -f usr/lib/jvm/java-"
+ str(options.jlink_java_version)
+ "-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts"
)
commands.append(
"ln -s --relative etc/ssl/certs/java/cacerts usr/lib/jvm/java-"
+ str(options.jlink_java_version)
+ "-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts"
)
return commands
3 changes: 2 additions & 1 deletion rockcraft/plugins/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import craft_parts
from craft_parts.plugins.plugins import PluginType

from .jlink_plugin import JLinkPlugin
from .python_plugin import PythonPlugin


Expand All @@ -29,4 +30,4 @@ def register() -> None:

def get_plugins() -> dict[str, PluginType]:
"""Get a dict of Rockcraft-specific plugins."""
return {"python": PythonPlugin}
return {"python": PythonPlugin, "jlink": JLinkPlugin}
Loading
Loading