From 276e66914ac9edbd56c35afb6f7c9f631a6a6959 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Wed, 28 Aug 2024 16:03:38 +1200 Subject: [PATCH 01/46] feat: jlink plugin Add initial implementation of jlink plugin. It creates Java runtime for a built project. If no jar files are found it generates an image for java.base module. --- rockcraft/plugins/jlink_plugin.py | 88 ++++++++++++++++++++++++ tests/unit/plugins/test_jlink_plugin.py | 91 +++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 rockcraft/plugins/jlink_plugin.py create mode 100644 tests/unit/plugins/test_jlink_plugin.py diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py new file mode 100644 index 000000000..7eaa5dd2d --- /dev/null +++ b/rockcraft/plugins/jlink_plugin.py @@ -0,0 +1,88 @@ +# -*- 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 . + +"""The Java runtime plugin.""" + +from overrides import override + +from typing import Any, Dict, Literal, List, Set, cast + +from craft_parts.plugins import Plugin, PluginProperties + + +class JLinkPluginProperties(PluginProperties): + """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 ["chisel"] + + @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 hasattr(options, "source") and isinstance(options.source, str): + commands.append("chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-"+str(options.jlink_java_version)+"-jre-headless_headless") + else: + commands.append("chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" + str(options.jlink_java_version) + "-jre-headless_headless") + + + 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}/jars -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 21 ${PROCESS_JARS}; else deps=java.base; fi)") + commands.append("install_root=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-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-21-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-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts") + commands.append("ln -s --relative etc/ssl/certs/java/cacerts usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts") + return commands diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py new file mode 100644 index 000000000..e4238c8d9 --- /dev/null +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -0,0 +1,91 @@ +# -*- 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 . + +from pathlib import Path + +import pytest +from craft_parts.infos import PartInfo, ProjectInfo +from craft_parts.parts import Part +from rockcraft.plugins.jlink_plugin import JLinkPlugin + + +@pytest.fixture() +def setup_method_fixture(): + def _setup_method_fixture(new_dir, properties=None): + if properties is None: + properties = {} + plugin_properties = JLinkPlugin.properties_class.unmarshal(properties) + part = Part("foo", {}) + + project_info = ProjectInfo(application_name="test", cache_dir=new_dir) + project_info._parallel_build_count = 42 + + part_info = PartInfo(project_info=project_info, part=part) + part_info._part_install_dir = Path("install/dir") + + return JLinkPlugin(properties=plugin_properties, part_info=part_info) + + return _setup_method_fixture + + +class TestPluginJLinkPlugin: + """JLink plugin tests.""" + + def test_get_build_snaps(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir) + assert plugin.get_build_snaps() == ["chisel"] + + def test_get_build_packages_default(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir) + assert plugin.get_build_packages() == { + "openjdk-21-jdk" + } + + def test_get_build_packages_17(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) + assert plugin.get_build_packages() == { + "openjdk-17-jdk" + } + + def test_get_build_environment(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir) + + assert plugin.get_build_environment() == {} + + def test_get_build_commands_default(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir) + + commands = plugin.get_build_commands() + assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" in commands + assert "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + + def test_get_build_commands_17(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) + + commands = plugin.get_build_commands() + assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_headless" in commands + + def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir, properties={"source": "foo"}) + + commands = plugin.get_build_commands() + assert "chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" in commands + assert "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + + + def test_get_build_commands_jars(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir, properties={"jlink-jars": ["foo.jar"]}) + assert "PROCESS_JARS=${CRAFT_STAGE}/foo.jar" in plugin.get_build_commands() From 1512a53f9a374096106189645e94f9758496a821 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Wed, 28 Aug 2024 16:59:25 +1200 Subject: [PATCH 02/46] feat: register jlink plugin --- rockcraft/plugins/__init__.py | 3 ++- rockcraft/plugins/register.py | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/rockcraft/plugins/__init__.py b/rockcraft/plugins/__init__.py index e526a7921..2ad494dbe 100644 --- a/rockcraft/plugins/__init__.py +++ b/rockcraft/plugins/__init__.py @@ -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"] diff --git a/rockcraft/plugins/register.py b/rockcraft/plugins/register.py index 2b6509856..c0777ce9d 100644 --- a/rockcraft/plugins/register.py +++ b/rockcraft/plugins/register.py @@ -20,6 +20,7 @@ from craft_parts.plugins.plugins import PluginType from .python_plugin import PythonPlugin +from .jlink_plugin import JLinkPlugin def register() -> None: @@ -29,4 +30,7 @@ def register() -> None: def get_plugins() -> dict[str, PluginType]: """Get a dict of Rockcraft-specific plugins.""" - return {"python": PythonPlugin} + return { + "python": PythonPlugin, + "jlink": JLinkPlugin + } From b7654fabb22ada409b1d6b8ffe28b95fca41196c Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 11:19:30 +1200 Subject: [PATCH 03/46] lint: jlink_plugin.py - reformat --- rockcraft/plugins/jlink_plugin.py | 61 ++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 7eaa5dd2d..408f467f3 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -16,22 +16,22 @@ """The Java runtime plugin.""" -from overrides import override - -from typing import Any, Dict, Literal, List, Set, cast +from typing import Dict, List, Literal, Set, cast from craft_parts.plugins import Plugin, PluginProperties +from overrides import override class JLinkPluginProperties(PluginProperties): """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""" + """Create a Java Runtime using JLink.""" properties_class = JLinkPluginProperties @@ -55,34 +55,59 @@ def get_build_commands(self) -> List[str]: commands = [] if hasattr(options, "source") and isinstance(options.source, str): - commands.append("chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-"+str(options.jlink_java_version)+"-jre-headless_headless") + commands.append( + "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" + + str(options.jlink_java_version) + + "-jre-headless_headless" + ) else: - commands.append("chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" + str(options.jlink_java_version) + "-jre-headless_headless") - + commands.append( + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" + + str(options.jlink_java_version) + + "-jre-headless_headless" + ) if len(options.jlink_jars) > 0: - jars = " ".join(["${CRAFT_STAGE}/" + x for x in options.jlink_jars]) + 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}/jars -type f -name *.jar)") + commands.append( + "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -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( + "(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 21 ${PROCESS_JARS}; else deps=java.base; fi)") - commands.append("install_root=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/") - - commands.append("rm -rf ${install_root} && jlink --add-modules ${deps} --output ${install_root}") + commands.append( + 'if [ "x${PROCESS_JARS}" == "x" ]; then deps=$(jdeps --class-path=${cpath} -q --recursive --ignore-missing-deps --print-module-deps --multi-release 21 ${PROCESS_JARS}; else deps=java.base; fi)' + ) + commands.append( + "install_root=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-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-21-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)") + commands.append( + "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-21-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( + "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-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts") - commands.append("ln -s --relative etc/ssl/certs/java/cacerts usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts") + commands.append( + "rm -f usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts" + ) + commands.append( + "ln -s --relative etc/ssl/certs/java/cacerts usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts" + ) return commands From 5d563477baf93c84a68c86624d9ab4a056ddd486 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 11:19:44 +1200 Subject: [PATCH 04/46] lint: register.py reformat --- rockcraft/plugins/register.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rockcraft/plugins/register.py b/rockcraft/plugins/register.py index c0777ce9d..37d20010f 100644 --- a/rockcraft/plugins/register.py +++ b/rockcraft/plugins/register.py @@ -19,8 +19,8 @@ import craft_parts from craft_parts.plugins.plugins import PluginType -from .python_plugin import PythonPlugin from .jlink_plugin import JLinkPlugin +from .python_plugin import PythonPlugin def register() -> None: @@ -30,7 +30,4 @@ def register() -> None: def get_plugins() -> dict[str, PluginType]: """Get a dict of Rockcraft-specific plugins.""" - return { - "python": PythonPlugin, - "jlink": JLinkPlugin - } + return {"python": PythonPlugin, "jlink": JLinkPlugin} From c0d841954095c617c7c25291d919dc48a87639f2 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 11:19:58 +1200 Subject: [PATCH 05/46] lint: test_jlink_plugin.py reformat --- tests/unit/plugins/test_jlink_plugin.py | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index e4238c8d9..df54c2d4d 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -50,15 +50,11 @@ def test_get_build_snaps(self, setup_method_fixture, new_dir): def test_get_build_packages_default(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) - assert plugin.get_build_packages() == { - "openjdk-21-jdk" - } + assert plugin.get_build_packages() == {"openjdk-21-jdk"} def test_get_build_packages_17(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) - assert plugin.get_build_packages() == { - "openjdk-17-jdk" - } + assert plugin.get_build_packages() == {"openjdk-17-jdk"} def test_get_build_environment(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) @@ -69,22 +65,34 @@ def test_get_build_commands_default(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) commands = plugin.get_build_commands() - assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" in commands - assert "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + assert ( + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" + in commands + ) + assert ( + "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + ) def test_get_build_commands_17(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) commands = plugin.get_build_commands() - assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_headless" in commands + assert ( + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_headless" + in commands + ) def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"source": "foo"}) commands = plugin.get_build_commands() - assert "chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" in commands - assert "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands - + assert ( + "chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" + in commands + ) + assert ( + "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + ) def test_get_build_commands_jars(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-jars": ["foo.jar"]}) From ea176cf39f31727fc740da0a746fccb03a6c5b5e Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 11:20:17 +1200 Subject: [PATCH 06/46] feat: test_jlink_plugin.py integration wip --- .../integration/plugins/test_jlink_plugin.py | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/integration/plugins/test_jlink_plugin.py diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py new file mode 100644 index 000000000..39fe4798a --- /dev/null +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -0,0 +1,134 @@ +# -*- 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 . + +import atexit +import os +import shutil +import textwrap +from pathlib import Path + +import pytest +from craft_application.models import DEVEL_BASE_INFOS +from rockcraft import plugins +from rockcraft.models.project import Project + +from tests.testing.project import create_project +from tests.util import ubuntu_only + +pytestmark = ubuntu_only + + +@pytest.fixture(autouse=True) +def setup_test(monkeypatch): + # Keep craft-parts from trying to refresh apt's cache, so that we can run + # this test as regular users. + monkeypatch.setenv("CRAFT_PARTS_PACKAGE_REFRESH", "0") + plugins.register() + + +def create_test_project(base, parts) -> Project: + build_base = None + if base in [info.current_devel_base for info in DEVEL_BASE_INFOS]: + build_base = "devel" + + return create_project(base=base, parts=parts, build_base=build_base) + + +def write_java_project(): + Path("pom.xml").write_text( + textwrap.dedent( + """ + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.3 + + + example + app + 0.0.1-SNAPSHOT + app + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + """ + ) + ) + + Path("app/src/main/java/example/app/Main.java").write_text( + textwrap.dedent( + """ + package example.app; + + import org.springframework.boot.SpringApplication; + import org.springframework.boot.autoconfigure.SpringBootApplication; + + @SpringBootApplication + public class Application { + + public Application(){ + System.out.println("Hello world!"); + } + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + } + """ + ) + ) + + +def test_jlink_plugin_base(tmp_path, run_lifecycle): + # pylint: disable=line-too-long + # chisel snap is confined to user home + user_home_tmp = Path(f"{os.path.expanduser("~")}/{os.path.basename(tmp_path)}") + atexit.register(lambda: shutil.rmtree(user_home_tmp)) + parts = { + "my-part": { + "plugin": "jlink", + "source": "https://github.com/vpa1977/chisel-releases", + "source-type": "git", + "source-branch": "24.04-openjdk-21-jre-headless", + } + } + project = create_test_project("ubuntu@24.04", parts) + run_lifecycle(project=project, work_dir=user_home_tmp) + java = user_home_tmp / "stage/usr/bin/java" + assert java.is_file() From 545b7078d43210252d657d430c5bb6c587fb8c3b Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 11:24:20 +1200 Subject: [PATCH 07/46] fix: jlink_plugin - dep commands --- rockcraft/plugins/jlink_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 408f467f3..f32be2e06 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -72,7 +72,7 @@ def get_build_commands(self) -> List[str]: commands.append(f"PROCESS_JARS={jars}") else: commands.append( - "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" + "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" ) # create temp folder @@ -85,7 +85,7 @@ def get_build_commands(self) -> List[str]: 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 21 ${PROCESS_JARS}; else deps=java.base; fi)' + 'if [ "x${PROCESS_JARS}" != "x" ]; then deps=$(jdeps --class-path=${cpath} -q --recursive --ignore-missing-deps --print-module-deps --multi-release 21 ${PROCESS_JARS}); else deps=java.base; fi' ) commands.append( "install_root=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/" From 3f375f8faa42b415e2754a8ebc13d3a08f521dbc Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:10:06 +1200 Subject: [PATCH 08/46] feat: rename slice to install dependencies --- rockcraft/plugins/jlink_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index f32be2e06..32b20afd0 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -64,7 +64,7 @@ def get_build_commands(self) -> List[str]: commands.append( "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" + str(options.jlink_java_version) - + "-jre-headless_headless" + + "-jre-headless_all" ) if len(options.jlink_jars) > 0: From 2ddf8cfe13224e999a69d314c604e4e681827ae7 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:18:16 +1200 Subject: [PATCH 09/46] feat: add dep slices --- rockcraft/plugins/jlink_plugin.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 32b20afd0..26dea4c1f 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -28,6 +28,7 @@ class JLinkPluginProperties(PluginProperties): plugin: Literal["jlink"] = "jlink" jlink_java_version: int = 21 jlink_jars: list[str] = [] + jlink_dep_slices: list[str] = [] class JLinkPlugin(Plugin): @@ -54,17 +55,17 @@ def get_build_commands(self) -> List[str]: options = cast(JLinkPluginProperties, self._options) commands = [] + slices = " ".join(options.jlink_dep_slices) + if len(slices) == 0: + slices = f"base-files_base openjdk-{options.jlink_java_version}-jre-headless_core" + if hasattr(options, "source") and isinstance(options.source, str): commands.append( - "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" - + str(options.jlink_java_version) - + "-jre-headless_headless" + "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} " + slices ) else: commands.append( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-" - + str(options.jlink_java_version) - + "-jre-headless_all" + "chisel cut --root ${CRAFT_PART_INSTALL} "+slices ) if len(options.jlink_jars) > 0: From 4e3615d0e51f48d6cfcb8f365bddb501aeac2c61 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:26:21 +1200 Subject: [PATCH 10/46] test: update test assertions --- tests/unit/plugins/test_jlink_plugin.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index df54c2d4d..624d3bf7c 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -66,11 +66,11 @@ def test_get_build_commands_default(self, setup_method_fixture, new_dir): commands = plugin.get_build_commands() assert ( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" in commands ) assert ( - "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands ) def test_get_build_commands_17(self, setup_method_fixture, new_dir): @@ -78,7 +78,7 @@ def test_get_build_commands_17(self, setup_method_fixture, new_dir): commands = plugin.get_build_commands() assert ( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_headless" + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_core" in commands ) @@ -87,13 +87,21 @@ def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): commands = plugin.get_build_commands() assert ( - "chisel cut --release `pwd` --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_headless" + "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" in commands ) assert ( - "PROCESS_JARS=$(find ${CRAFT_STAGE}/jars -type f -name *.jar)" in commands + "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands ) def test_get_build_commands_jars(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-jars": ["foo.jar"]}) assert "PROCESS_JARS=${CRAFT_STAGE}/foo.jar" in plugin.get_build_commands() + + def test_get_build_commands_deps(self, setup_method_fixture, new_dir): + plugin = setup_method_fixture(new_dir, properties={"jlink-dep-slices": ["base-files_base"]}) + commands = plugin.get_build_commands() + assert ( + "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base" + in commands + ) From ab8598f57bfa3d7102861d000d5f23ea3535fe7a Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:35:55 +1200 Subject: [PATCH 11/46] test: drop project test. --- .../integration/plugins/test_jlink_plugin.py | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 39fe4798a..d21ed5569 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -47,74 +47,6 @@ def create_test_project(base, parts) -> Project: return create_project(base=base, parts=parts, build_base=build_base) -def write_java_project(): - Path("pom.xml").write_text( - textwrap.dedent( - """ - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.3 - - - example - app - 0.0.1-SNAPSHOT - app - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-actuator - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - """ - ) - ) - - Path("app/src/main/java/example/app/Main.java").write_text( - textwrap.dedent( - """ - package example.app; - - import org.springframework.boot.SpringApplication; - import org.springframework.boot.autoconfigure.SpringBootApplication; - - @SpringBootApplication - public class Application { - - public Application(){ - System.out.println("Hello world!"); - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - - } - """ - ) - ) - - def test_jlink_plugin_base(tmp_path, run_lifecycle): # pylint: disable=line-too-long # chisel snap is confined to user home From 7f76cc545f6c3724b7e6d1877d6912b28b2824ee Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:39:04 +1200 Subject: [PATCH 12/46] lint: reformat jlink_plugin --- rockcraft/plugins/jlink_plugin.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 26dea4c1f..8ee1c6ce0 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -64,17 +64,13 @@ def get_build_commands(self) -> List[str]: "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} " + slices ) else: - commands.append( - "chisel cut --root ${CRAFT_PART_INSTALL} "+slices - ) + commands.append("chisel cut --root ${CRAFT_PART_INSTALL} " + slices) 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)" - ) + commands.append("PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)") # create temp folder commands.append("mkdir -p ${CRAFT_PART_BUILD}/tmp") From a4844ed5bb2682758bb567dd4995f88bcaa67000 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:39:17 +1200 Subject: [PATCH 13/46] lint: reformat test_jlink_plugin --- tests/unit/plugins/test_jlink_plugin.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index 624d3bf7c..cfa19ed11 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -69,9 +69,7 @@ def test_get_build_commands_default(self, setup_method_fixture, new_dir): "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" in commands ) - assert ( - "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands - ) + assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands def test_get_build_commands_17(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) @@ -90,18 +88,15 @@ def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" in commands ) - assert ( - "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands - ) + assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands def test_get_build_commands_jars(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-jars": ["foo.jar"]}) assert "PROCESS_JARS=${CRAFT_STAGE}/foo.jar" in plugin.get_build_commands() def test_get_build_commands_deps(self, setup_method_fixture, new_dir): - plugin = setup_method_fixture(new_dir, properties={"jlink-dep-slices": ["base-files_base"]}) - commands = plugin.get_build_commands() - assert ( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base" - in commands + plugin = setup_method_fixture( + new_dir, properties={"jlink-dep-slices": ["base-files_base"]} ) + commands = plugin.get_build_commands() + assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base" in commands From 7798ac87a1a9b92e37c01149b9cddbc1d1a8c19b Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:41:19 +1200 Subject: [PATCH 14/46] test: fix directory. --- tests/integration/plugins/test_jlink_plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index d21ed5569..0ce70646e 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -50,7 +50,9 @@ def create_test_project(base, parts) -> Project: def test_jlink_plugin_base(tmp_path, run_lifecycle): # pylint: disable=line-too-long # chisel snap is confined to user home - user_home_tmp = Path(f"{os.path.expanduser("~")}/{os.path.basename(tmp_path)}") + home = os.path.expanduser("~") + basename = os.path.basename(tmp_path) + user_home_tmp = Path(f"{home}/{basename}") atexit.register(lambda: shutil.rmtree(user_home_tmp)) parts = { "my-part": { From 2e18adb940dc22ff5c0e59fee6f6313627162b05 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:55:22 +1200 Subject: [PATCH 15/46] lint(test_jlink_plugin.py): unused import. --- tests/integration/plugins/test_jlink_plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 0ce70646e..9f05cf798 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -17,7 +17,6 @@ import atexit import os import shutil -import textwrap from pathlib import Path import pytest From 3411fbd524185348ad5290ab07139659a61d8d01 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 13:57:17 +1200 Subject: [PATCH 16/46] deps: add git and openjdk-21-jdk --- .github/workflows/tests.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 754c5bb95..6906bb78e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -81,7 +81,7 @@ jobs: 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 @@ -99,4 +99,3 @@ jobs: - name: Run integration tests run: | make test-integrations - From ceb65d19a06073fa932d32f91d05974c551934e9 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:01:57 +1200 Subject: [PATCH 17/46] lint(jlink_plugin.py): mypy fixes. --- rockcraft/plugins/jlink_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 8ee1c6ce0..9cc88f7e3 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -22,7 +22,7 @@ from overrides import override -class JLinkPluginProperties(PluginProperties): +class JLinkPluginProperties(PluginProperties, frozen=True): """The part properties used by the JLink plugin.""" plugin: Literal["jlink"] = "jlink" @@ -47,7 +47,7 @@ def get_build_environment(self) -> Dict[str, str]: @override def get_build_snaps(self) -> Set[str]: - return ["chisel"] + return {} @override def get_build_commands(self) -> List[str]: From 6e875b8c17d0ab00d972b37ab4186d235d539bff Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:07:48 +1200 Subject: [PATCH 18/46] fix(jlink_plugin.py): drop chisel dependency. --- rockcraft/plugins/jlink_plugin.py | 2 +- tests/unit/plugins/test_jlink_plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 9cc88f7e3..219ef076f 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -47,7 +47,7 @@ def get_build_environment(self) -> Dict[str, str]: @override def get_build_snaps(self) -> Set[str]: - return {} + return set() @override def get_build_commands(self) -> List[str]: diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index cfa19ed11..1a34c182f 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -46,7 +46,7 @@ class TestPluginJLinkPlugin: def test_get_build_snaps(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) - assert plugin.get_build_snaps() == ["chisel"] + assert plugin.get_build_snaps() == {} def test_get_build_packages_default(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) From a776464abc3ec855895acaf8b55dd2f392f02cdc Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:10:01 +1200 Subject: [PATCH 19/46] test(test_jlink_plugin.py): fix assertion --- tests/unit/plugins/test_jlink_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index 1a34c182f..2c295f269 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -46,7 +46,7 @@ class TestPluginJLinkPlugin: def test_get_build_snaps(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) - assert plugin.get_build_snaps() == {} + assert plugin.get_build_snaps() == set() def test_get_build_packages_default(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir) From 66ab695de1afc08eecd65e4af59765b8f606b10c Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:31:13 +1200 Subject: [PATCH 20/46] deps: install chisel snap --- .github/workflows/tests.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6906bb78e..10836a9b2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -77,6 +77,9 @@ jobs: - name: Install dependencies run: | + echo "::group::snap install" + sudo snap install chisel + echo "::endgroup::" echo "::group::apt-get update" sudo apt-get update echo "::endgroup::" From 212c9fd9f2422994dc5cb4d82750e0fc01f757a1 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:32:26 +1200 Subject: [PATCH 21/46] deps: install chisel snap --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 10836a9b2..341aead9a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -78,7 +78,7 @@ jobs: - name: Install dependencies run: | echo "::group::snap install" - sudo snap install chisel + sudo snap install chisel --edge echo "::endgroup::" echo "::group::apt-get update" sudo apt-get update From 48b54f4356fbf55bdc126d4f06a202cbf5a2ef6f Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 14:42:06 +1200 Subject: [PATCH 22/46] lint(test_jlink_plugin.py): remove exclusion. --- tests/integration/plugins/test_jlink_plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 9f05cf798..419e586cb 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -47,7 +47,6 @@ def create_test_project(base, parts) -> Project: def test_jlink_plugin_base(tmp_path, run_lifecycle): - # pylint: disable=line-too-long # chisel snap is confined to user home home = os.path.expanduser("~") basename = os.path.basename(tmp_path) From efde661b345d11e26ca56fa3957d33a58e686c45 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 15:17:57 +1200 Subject: [PATCH 23/46] test(jlink_plugin): add integration test with jar --- .../integration/plugins/test_jlink_plugin.py | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 419e586cb..7baa05cdf 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -17,6 +17,7 @@ import atexit import os import shutil +import subprocess from pathlib import Path import pytest @@ -45,13 +46,57 @@ def create_test_project(base, parts) -> Project: return create_project(base=base, parts=parts, build_base=build_base) - -def test_jlink_plugin_base(tmp_path, run_lifecycle): +def get_tmp_path(tmp_path: str) -> Path: # chisel snap is confined to user home home = os.path.expanduser("~") basename = os.path.basename(tmp_path) - user_home_tmp = Path(f"{home}/{basename}") - atexit.register(lambda: shutil.rmtree(user_home_tmp)) + ret = Path(f"{home}/{basename}") + atexit.register(lambda: shutil.rmtree(ret)) + return ret + + +def test_jlink_plugin_with_jar(tmp_path, run_lifecycle): + """Test that jlink produces tailored modules""" + user_home_tmp = get_tmp_path(tmp_path) + parts = { + "my-part": { + "plugin": "jlink", + "source": "https://github.com/vpa1977/chisel-releases", + "source-type": "git", + "source-branch": "24.04-openjdk-21-jre-headless", + "jlink-jars" : ["test.jar"], + "after": ["stage-jar"] + }, + "stage-jar" : { + "plugin": "dump", + "source": ".", + } + } + # build test jar + Path(f"Test.java").write_text( + """ + import javax.swing.*; + public class Test { + public static void main(String[] args) { + new JFrame("foo").setVisible(true); + } + } + """ + ) + subprocess.run(["javac", "Test.java"], check=True, capture_output=True) + subprocess.run(["jar", "cvf", "test.jar", "Test.class"],check=True, capture_output=True) + atexit.register(lambda: os.remove("Test.class")) + atexit.register(lambda: os.remove("Test.java")) + atexit.register(lambda: os.remove("test.jar")) + + project = create_test_project("ubuntu@24.04", parts) + run_lifecycle(project=project, work_dir=user_home_tmp) + # java.desktop module should be included in the image + assert len(list(Path(f"{user_home_tmp}/stage/usr/lib/jvm/").rglob("libawt.so"))) > 0 + + +def test_jlink_plugin_base(tmp_path, run_lifecycle): + """Test that jlink produces base image""" parts = { "my-part": { "plugin": "jlink", @@ -60,6 +105,7 @@ def test_jlink_plugin_base(tmp_path, run_lifecycle): "source-branch": "24.04-openjdk-21-jre-headless", } } + user_home_tmp = get_tmp_path(tmp_path) project = create_test_project("ubuntu@24.04", parts) run_lifecycle(project=project, work_dir=user_home_tmp) java = user_home_tmp / "stage/usr/bin/java" From 943d333dcec3748a0e5f3a66aac30144c09aa482 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 15:29:52 +1200 Subject: [PATCH 24/46] fix: add minimal default classpath --- rockcraft/plugins/jlink_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 219ef076f..aa9d4e7bb 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -79,7 +79,7 @@ def get_build_commands(self) -> List[str]: "(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("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 21 ${PROCESS_JARS}); else deps=java.base; fi' From e2ce4b5da13ae6537cace0b8f14a06ff7e0997f8 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 15:33:10 +1200 Subject: [PATCH 25/46] lint: formatting --- tests/integration/plugins/test_jlink_plugin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 7baa05cdf..3047c02ce 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -46,6 +46,7 @@ def create_test_project(base, parts) -> Project: return create_project(base=base, parts=parts, build_base=build_base) + def get_tmp_path(tmp_path: str) -> Path: # chisel snap is confined to user home home = os.path.expanduser("~") @@ -64,13 +65,13 @@ def test_jlink_plugin_with_jar(tmp_path, run_lifecycle): "source": "https://github.com/vpa1977/chisel-releases", "source-type": "git", "source-branch": "24.04-openjdk-21-jre-headless", - "jlink-jars" : ["test.jar"], - "after": ["stage-jar"] + "jlink-jars": ["test.jar"], + "after": ["stage-jar"], }, - "stage-jar" : { + "stage-jar": { "plugin": "dump", "source": ".", - } + }, } # build test jar Path(f"Test.java").write_text( @@ -82,9 +83,11 @@ def test_jlink_plugin_with_jar(tmp_path, run_lifecycle): } } """ - ) + ) subprocess.run(["javac", "Test.java"], check=True, capture_output=True) - subprocess.run(["jar", "cvf", "test.jar", "Test.class"],check=True, capture_output=True) + subprocess.run( + ["jar", "cvf", "test.jar", "Test.class"], check=True, capture_output=True + ) atexit.register(lambda: os.remove("Test.class")) atexit.register(lambda: os.remove("Test.java")) atexit.register(lambda: os.remove("test.jar")) From a288da31652245007c9078741d6910d0f718c081 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Thu, 29 Aug 2024 15:39:34 +1200 Subject: [PATCH 26/46] lint(test_jlink_plugin.py): remove f-string --- tests/integration/plugins/test_jlink_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/plugins/test_jlink_plugin.py b/tests/integration/plugins/test_jlink_plugin.py index 3047c02ce..452257344 100644 --- a/tests/integration/plugins/test_jlink_plugin.py +++ b/tests/integration/plugins/test_jlink_plugin.py @@ -74,7 +74,7 @@ def test_jlink_plugin_with_jar(tmp_path, run_lifecycle): }, } # build test jar - Path(f"Test.java").write_text( + Path("Test.java").write_text( """ import javax.swing.*; public class Test { From 3f2f61ad1c5c584a3b3473c2678f62ff831aba01 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 08:35:13 +1200 Subject: [PATCH 27/46] fix(jlink_plugin): capitalize variables --- rockcraft/plugins/jlink_plugin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index aa9d4e7bb..38c94e360 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -78,18 +78,18 @@ def get_build_commands(self) -> List[str]: 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("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 21 ${PROCESS_JARS}); else deps=java.base; fi' + 'if [ "x${PROCESS_JARS}" != "x" ]; then deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps --print-module-deps --multi-release 21 ${PROCESS_JARS}); else deps=java.base; fi' ) commands.append( - "install_root=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/" + "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/" ) commands.append( - "rm -rf ${install_root} && jlink --add-modules ${deps} --output ${install_root}" + "rm -rf ${INSTALL_ROOT} && jlink --add-modules ${deps} --output ${INSTALL_ROOT}" ) # create /usr/bin/java link commands.append( From dbe14dd2d7aed4ef7c4923d27efcfc96347ab0a8 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 08:54:04 +1200 Subject: [PATCH 28/46] fix(jlink_plugin): use options.jlink_java_version Use options.jlink_java_version in plugin to specify paths. Update test assertions. --- rockcraft/plugins/jlink_plugin.py | 24 +++++++++++++++++++----- tests/unit/plugins/test_jlink_plugin.py | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 38c94e360..18566266a 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -82,10 +82,18 @@ def get_build_commands(self) -> List[str]: 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 21 ${PROCESS_JARS}); else deps=java.base; fi' + """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-21-openjdk-${CRAFT_TARGET_ARCH}/" + "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-" + + str(options.jlink_java_version) + + "-openjdk-${CRAFT_TARGET_ARCH}/" ) commands.append( @@ -93,7 +101,9 @@ def get_build_commands(self) -> List[str]: ) # create /usr/bin/java link commands.append( - "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" + "(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 @@ -102,9 +112,13 @@ def get_build_commands(self) -> List[str]: ) commands.append("cd ${CRAFT_PART_INSTALL}") commands.append( - "rm -f usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts" + "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-21-openjdk-${CRAFT_TARGET_ARCH}/lib/security/cacerts" + "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 diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index 2c295f269..cc698c141 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -79,6 +79,23 @@ def test_get_build_commands_17(self, setup_method_fixture, new_dir): "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_core" in commands ) + assert ( + """if [ "x${PROCESS_JARS}" != "x" ]; then + deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps \ + --print-module-deps --multi-release 17\ + ${PROCESS_JARS}); else deps=java.base; + fi + """ + in commands + ) + assert ( + "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-17-openjdk-${CRAFT_TARGET_ARCH}/" + in commands + ) + assert ( + "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-17-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" + in commands + ) def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"source": "foo"}) From d23ed5fa333e851f9d3f2efd030be8123df537da Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 09:12:26 +1200 Subject: [PATCH 29/46] lint(jlink_plugin): fix command formatting --- rockcraft/plugins/jlink_plugin.py | 3 +-- tests/unit/plugins/test_jlink_plugin.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 18566266a..7cad5fca8 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -86,8 +86,7 @@ def get_build_commands(self) -> List[str]: 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 + + """ ${PROCESS_JARS}); else deps=java.base; fi """ ) commands.append( diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index cc698c141..de01e47bd 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -82,9 +82,7 @@ def test_get_build_commands_17(self, setup_method_fixture, new_dir): assert ( """if [ "x${PROCESS_JARS}" != "x" ]; then deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps \ - --print-module-deps --multi-release 17\ - ${PROCESS_JARS}); else deps=java.base; - fi + --print-module-deps --multi-release 17 ${PROCESS_JARS}); else deps=java.base; fi """ in commands ) From 3ba87e200b8ef3b6a665d7cb5c2197743e93ef82 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 15:08:32 +1200 Subject: [PATCH 30/46] fix(jlink_plugin): drop chiselling dependencies Until https://github.com/canonical/craft-parts/issues/833 is implemented, the Java dependencies will be chiselled by a separate part. JLink plugin may implement validation of the dependencies in a separate PR. --- rockcraft/plugins/jlink_plugin.py | 11 ------ tests/unit/plugins/test_jlink_plugin.py | 46 ++++++------------------- 2 files changed, 11 insertions(+), 46 deletions(-) diff --git a/rockcraft/plugins/jlink_plugin.py b/rockcraft/plugins/jlink_plugin.py index 7cad5fca8..3141ac8ec 100644 --- a/rockcraft/plugins/jlink_plugin.py +++ b/rockcraft/plugins/jlink_plugin.py @@ -28,7 +28,6 @@ class JLinkPluginProperties(PluginProperties, frozen=True): plugin: Literal["jlink"] = "jlink" jlink_java_version: int = 21 jlink_jars: list[str] = [] - jlink_dep_slices: list[str] = [] class JLinkPlugin(Plugin): @@ -55,16 +54,6 @@ def get_build_commands(self) -> List[str]: options = cast(JLinkPluginProperties, self._options) commands = [] - slices = " ".join(options.jlink_dep_slices) - if len(slices) == 0: - slices = f"base-files_base openjdk-{options.jlink_java_version}-jre-headless_core" - - if hasattr(options, "source") and isinstance(options.source, str): - commands.append( - "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} " + slices - ) - else: - commands.append("chisel cut --root ${CRAFT_PART_INSTALL} " + slices) if len(options.jlink_jars) > 0: jars = " ".join(["${CRAFT_STAGE}/" + x for x in options.jlink_jars]) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index de01e47bd..089784b2e 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -61,57 +61,33 @@ def test_get_build_environment(self, setup_method_fixture, new_dir): assert plugin.get_build_environment() == {} - def test_get_build_commands_default(self, setup_method_fixture, new_dir): - plugin = setup_method_fixture(new_dir) + @pytest.mark.parametrize("version", [None, "21", "17"]) + def test_get_build_commands(self, setup_method_fixture, new_dir, version): - commands = plugin.get_build_commands() - assert ( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" - in commands - ) - assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands - - def test_get_build_commands_17(self, setup_method_fixture, new_dir): - plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": "17"}) + if version is None: + plugin = setup_method_fixture(new_dir) + version = "21" + else: + plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": version}) commands = plugin.get_build_commands() - assert ( - "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base openjdk-17-jre-headless_core" - in commands - ) + assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands assert ( """if [ "x${PROCESS_JARS}" != "x" ]; then deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps \ - --print-module-deps --multi-release 17 ${PROCESS_JARS}); else deps=java.base; fi + --print-module-deps --multi-release """+version+ """ ${PROCESS_JARS}); else deps=java.base; fi """ in commands ) assert ( - "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-17-openjdk-${CRAFT_TARGET_ARCH}/" + "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-"+version+ "-openjdk-${CRAFT_TARGET_ARCH}/" in commands ) assert ( - "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-17-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" + "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-"+version+ "-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" in commands ) - def test_get_build_commands_chisel_source(self, setup_method_fixture, new_dir): - plugin = setup_method_fixture(new_dir, properties={"source": "foo"}) - - commands = plugin.get_build_commands() - assert ( - "chisel cut --release ./ --root ${CRAFT_PART_INSTALL} base-files_base openjdk-21-jre-headless_core" - in commands - ) - assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands - def test_get_build_commands_jars(self, setup_method_fixture, new_dir): plugin = setup_method_fixture(new_dir, properties={"jlink-jars": ["foo.jar"]}) assert "PROCESS_JARS=${CRAFT_STAGE}/foo.jar" in plugin.get_build_commands() - - def test_get_build_commands_deps(self, setup_method_fixture, new_dir): - plugin = setup_method_fixture( - new_dir, properties={"jlink-dep-slices": ["base-files_base"]} - ) - commands = plugin.get_build_commands() - assert "chisel cut --root ${CRAFT_PART_INSTALL} base-files_base" in commands From 4c734fcdf37ae889877d09bc79874e2cbdb404cc Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 15:27:26 +1200 Subject: [PATCH 31/46] lint(test_jlink_plugin): reformat. --- tests/unit/plugins/test_jlink_plugin.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/unit/plugins/test_jlink_plugin.py b/tests/unit/plugins/test_jlink_plugin.py index 089784b2e..4bbf14acd 100644 --- a/tests/unit/plugins/test_jlink_plugin.py +++ b/tests/unit/plugins/test_jlink_plugin.py @@ -68,23 +68,31 @@ def test_get_build_commands(self, setup_method_fixture, new_dir, version): plugin = setup_method_fixture(new_dir) version = "21" else: - plugin = setup_method_fixture(new_dir, properties={"jlink-java-version": version}) + plugin = setup_method_fixture( + new_dir, properties={"jlink-java-version": version} + ) commands = plugin.get_build_commands() assert "PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)" in commands assert ( """if [ "x${PROCESS_JARS}" != "x" ]; then deps=$(jdeps --class-path=${CPATH} -q --recursive --ignore-missing-deps \ - --print-module-deps --multi-release """+version+ """ ${PROCESS_JARS}); else deps=java.base; fi + --print-module-deps --multi-release """ + + version + + """ ${PROCESS_JARS}); else deps=java.base; fi """ in commands ) assert ( - "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-"+version+ "-openjdk-${CRAFT_TARGET_ARCH}/" + "INSTALL_ROOT=${CRAFT_PART_INSTALL}/usr/lib/jvm/java-" + + version + + "-openjdk-${CRAFT_TARGET_ARCH}/" in commands ) assert ( - "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-"+version+ "-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" + "(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative usr/lib/jvm/java-" + + version + + "-openjdk-${CRAFT_TARGET_ARCH}/bin/java usr/bin/)" in commands ) From 579a50d78b06697a4822e1961f2c362fecc54983 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 08:22:31 +1200 Subject: [PATCH 32/46] feat(spring_boot_framework): wip implementation --- rockcraft/extensions/__init__.py | 2 + rockcraft/extensions/spring_boot.py | 109 +++++++++++++++++++++ tests/unit/extensions/test_spring_boot.py | 113 ++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 rockcraft/extensions/spring_boot.py create mode 100644 tests/unit/extensions/test_spring_boot.py diff --git a/rockcraft/extensions/__init__.py b/rockcraft/extensions/__init__.py index 63d7acd76..6ea8de7a3 100644 --- a/rockcraft/extensions/__init__.py +++ b/rockcraft/extensions/__init__.py @@ -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__ = [ @@ -32,3 +33,4 @@ register("django-framework", DjangoFramework) register("flask-framework", FlaskFramework) register("go-framework", GoFramework) +register("spring-boot-framework", SpringBootFramework) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py new file mode 100644 index 000000000..3f2f8adb3 --- /dev/null +++ b/rockcraft/extensions/spring_boot.py @@ -0,0 +1,109 @@ +# -*- 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 . + +"""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 .extension import Extension + +class SpringBootFramework(Extension): + + def _check_project(self): + """Ensure that either pom.xml or gradlew is present.""" + if not os.path.exists(f"{self.project_root}/pom.xml") and not os.path.exists(f"{self.project_root}/gradlew"): + pass + + @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", "ubuntu@24.04", "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 {"run_user": "_daemon_",} + + + 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": ["default-jdk", "maven"], + "override-build": """ + maven package + mkdir -p ${CRAFT_PART_INSTALL}/jar + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + craftctl default + """ + } + elif os.path.exists(f"{self.project_root}/gradlew"): + return { + "plugin": "nil", + "source": ".", + "source-type": "local", + "build-packages": ["default-jdk"], + "override-build" : """ + ./gradlew jar --no-daemon + mkdir -p ${CRAFT_PART_INSTALL}/jar + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + 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" ], + # temporary entries until chisel-releases for openjdk + # are merged upstream + "source": "https://github.com/vpa1977/chisel-releases", + "source-type": "git", + "source-branch": "24.04-openjdk-21-jre-headless", + } + 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() + } diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py new file mode 100644 index 000000000..f750bc7aa --- /dev/null +++ b/tests/unit/extensions/test_spring_boot.py @@ -0,0 +1,113 @@ +# -*- 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 . +import textwrap + +import pytest +from rockcraft import extensions +from rockcraft.errors import ExtensionError + + +@pytest.fixture(name="spring_boot_input_yaml") +def spring_boot_input_yaml_fixture(): + return { + "name": "springboot", + "base": "ubuntu@24.04", + "platforms": {"amd64": {}}, + "extensions": ["spring-boot-framework"], + } + + +@pytest.fixture +def spring_boot_extension(mock_extensions, monkeypatch): + monkeypatch.setenv("ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS", "1") + extensions.register("spring-boot-framework", extensions.SpringBootFramework) + + +@pytest.mark.usefixtures("spring_boot_extension") +def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): + (tmp_path / "pom.xml").write_text("") + applied = extensions.apply_extensions(tmp_path, spring_boot_input_yaml) + + assert applied == { + "base": "ubuntu@24.04", + "name": "springboot", + "platforms": {"amd64": {}}, + "run_user": "_daemon_", + "parts": { + "spring-boot-framework/install-app": { + "plugin": "nil", + "source": ".", + "source-type": "local", + "build-packages": ["default-jdk", "maven"], + "override-build": """ + maven package + mkdir -p ${CRAFT_PART_INSTALL}/jar + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + craftctl default + """, + }, + "spring-boot-framework/runtime": { + "plugin": "jlink", + "after": ["spring-boot-framework/install-app"], + "source": "https://github.com/vpa1977/chisel-releases", + "source-type": "git", + "source-branch": "24.04-openjdk-21-jre-headless", + }, + }, + } + + +@pytest.mark.usefixtures("spring_boot_extension") +def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): + (tmp_path / "gradlew").write_text("") + applied = extensions.apply_extensions(tmp_path, spring_boot_input_yaml) + + assert applied == { + "base": "ubuntu@24.04", + "name": "springboot", + "platforms": {"amd64": {}}, + "run_user": "_daemon_", + "parts": { + "spring-boot-framework/install-app": { + "plugin": "nil", + "source": ".", + "source-type": "local", + "build-packages": ["default-jdk"], + "override-build": """ + ./gradlew jar --no-daemon + mkdir -p ${CRAFT_PART_INSTALL}/jar + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + craftctl default + """, + }, + "spring-boot-framework/runtime": { + "plugin": "jlink", + "after": ["spring-boot-framework/install-app"], + "source": "https://github.com/vpa1977/chisel-releases", + "source-type": "git", + "source-branch": "24.04-openjdk-21-jre-headless", + }, + }, + } + + +@pytest.mark.usefixtures("spring_boot_extension") +def test_spring_boot_extension_no_project_error(tmp_path, spring_boot_input_yaml): + (tmp_path / "somefile").write_text("random text") + with pytest.raises(ExtensionError) as exc: + extensions.apply_extensions(tmp_path, spring_boot_input_yaml) + assert str(exc.value) == "missing pom.xml or gradlew file" + assert str(exc.value.doc_slug) == "/reference/extensions/spring-boot-framework" From 71ee60eb33ba81f0f384a53142fb5822bb64e808 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 18:37:27 +1200 Subject: [PATCH 33/46] feat(spring-boot-framework): add runtime-deps part spring-boot-framework extension will use separate part to stage dependencies for Java runtime until stage-packages is implemented for plugins. --- rockcraft/extensions/spring_boot.py | 54 +++++++++++++++++------ tests/unit/extensions/test_spring_boot.py | 34 +++++++++++++- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 3f2f8adb3..17955ca87 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -22,14 +22,22 @@ 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 not os.path.exists(f"{self.project_root}/pom.xml") and not os.path.exists(f"{self.project_root}/gradlew"): - pass + 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: @@ -55,8 +63,27 @@ def get_part_snippet(self) -> Dict[str, Any]: def get_root_snippet(self) -> Dict[str, Any]: self._check_project() - return {"run_user": "_daemon_",} + return { + "run_user": "_daemon_", + } + 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} \ + 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", {}): @@ -71,7 +98,7 @@ def gen_install_app_part(self) -> Dict[str, Any]: mkdir -p ${CRAFT_PART_INSTALL}/jar find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; craftctl default - """ + """, } elif os.path.exists(f"{self.project_root}/gradlew"): return { @@ -79,12 +106,12 @@ def gen_install_app_part(self) -> Dict[str, Any]: "source": ".", "source-type": "local", "build-packages": ["default-jdk"], - "override-build" : """ + "override-build": """ ./gradlew jar --no-daemon mkdir -p ${CRAFT_PART_INSTALL}/jar find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; craftctl default - """ + """, } return {} @@ -92,18 +119,17 @@ 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" ], - # temporary entries until chisel-releases for openjdk - # are merged upstream - "source": "https://github.com/vpa1977/chisel-releases", - "source-type": "git", - "source-branch": "24.04-openjdk-21-jre-headless", + "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/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(), } diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index f750bc7aa..46568de61 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -61,10 +61,25 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): }, "spring-boot-framework/runtime": { "plugin": "jlink", - "after": ["spring-boot-framework/install-app"], + "after": [ + "spring-boot-framework/install-app", + "spring-boot-framework/runtime-deps", + ], + }, + "spring-boot-framework/runtime-deps": { + "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} \ + libc6_libs \ + libgcc-s1_libs \ + libstdc++6_libs \ + zlib1g_libs \ + libnss3_libs + craftctl default + """, }, }, } @@ -95,10 +110,25 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): }, "spring-boot-framework/runtime": { "plugin": "jlink", - "after": ["spring-boot-framework/install-app"], + "after": [ + "spring-boot-framework/install-app", + "spring-boot-framework/runtime-deps", + ], + }, + "spring-boot-framework/runtime-deps": { + "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} \ + libc6_libs \ + libgcc-s1_libs \ + libstdc++6_libs \ + zlib1g_libs \ + libnss3_libs + craftctl default + """, }, }, } From 89ec22bf0e69d173b4c77873f87e4303fa386b3c Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 18:51:46 +1200 Subject: [PATCH 34/46] fix(spring-boot-framework): set JAVA_HOME in build current build path has staged area before build area. --- rockcraft/extensions/spring_boot.py | 12 +++++++++--- tests/unit/extensions/test_spring_boot.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 17955ca87..445d28dc2 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -92,9 +92,12 @@ def gen_install_app_part(self) -> Dict[str, Any]: "plugin": "nil", "source": ".", "source-type": "local", - "build-packages": ["default-jdk", "maven"], + "build-packages": ["openjdk-21-jdk", "maven"], + "build-environment" : { + "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + }, "override-build": """ - maven package + mvn package mkdir -p ${CRAFT_PART_INSTALL}/jar find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; craftctl default @@ -105,7 +108,10 @@ def gen_install_app_part(self) -> Dict[str, Any]: "plugin": "nil", "source": ".", "source-type": "local", - "build-packages": ["default-jdk"], + "build-packages": ["openjdk-21-jdk"], + "build-environment" : { + "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + }, "override-build": """ ./gradlew jar --no-daemon mkdir -p ${CRAFT_PART_INSTALL}/jar diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 46568de61..60ecab6d2 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -51,9 +51,12 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "plugin": "nil", "source": ".", "source-type": "local", - "build-packages": ["default-jdk", "maven"], + "build-packages": ["openjdk-21-jdk", "maven"], + "build-environment" : { + "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + }, "override-build": """ - maven package + mvn package mkdir -p ${CRAFT_PART_INSTALL}/jar find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; craftctl default @@ -100,7 +103,10 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "plugin": "nil", "source": ".", "source-type": "local", - "build-packages": ["default-jdk"], + "build-packages": ["openjdk-21-jdk"], + "build-environment" : { + "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + }, "override-build": """ ./gradlew jar --no-daemon mkdir -p ${CRAFT_PART_INSTALL}/jar From e3905c281ddadc4ea3e6d3f951719b0e05368146 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 19:06:04 +1200 Subject: [PATCH 35/46] lint(spring-boot-framework): reformat --- rockcraft/extensions/spring_boot.py | 8 ++++---- tests/unit/extensions/test_spring_boot.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 445d28dc2..9a52592ce 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -93,8 +93,8 @@ def gen_install_app_part(self) -> Dict[str, Any]: "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], - "build-environment" : { - "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "build-environment": { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" }, "override-build": """ mvn package @@ -109,8 +109,8 @@ def gen_install_app_part(self) -> Dict[str, Any]: "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk"], - "build-environment" : { - "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "build-environment": { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" }, "override-build": """ ./gradlew jar --no-daemon diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 60ecab6d2..2f03b16b2 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -52,8 +52,8 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], - "build-environment" : { - "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "build-environment": { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" }, "override-build": """ mvn package @@ -104,8 +104,8 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk"], - "build-environment" : { - "JAVA_HOME" : "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "build-environment": { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" }, "override-build": """ ./gradlew jar --no-daemon From 9ad391e741a6a2be96e3608d733c0593d1cbd7ad Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 19:11:29 +1200 Subject: [PATCH 36/46] fix(spring-boot-framework): fix build-environment --- rockcraft/extensions/spring_boot.py | 16 ++++++++++------ tests/unit/extensions/test_spring_boot.py | 12 ++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 9a52592ce..1d9ee3ca9 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -93,9 +93,11 @@ def gen_install_app_part(self) -> Dict[str, Any]: "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], - "build-environment": { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" - }, + "build-environment": [ + { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + } + ], "override-build": """ mvn package mkdir -p ${CRAFT_PART_INSTALL}/jar @@ -109,9 +111,11 @@ def gen_install_app_part(self) -> Dict[str, Any]: "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk"], - "build-environment": { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" - }, + "build-environment": [ + { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + } + ], "override-build": """ ./gradlew jar --no-daemon mkdir -p ${CRAFT_PART_INSTALL}/jar diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 2f03b16b2..ccb8ca0ce 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -52,9 +52,9 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], - "build-environment": { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" - }, + "build-environment": [ + {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}"} + ], "override-build": """ mvn package mkdir -p ${CRAFT_PART_INSTALL}/jar @@ -104,9 +104,9 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "source": ".", "source-type": "local", "build-packages": ["openjdk-21-jdk"], - "build-environment": { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" - }, + "build-environment": [ + {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}"} + ], "override-build": """ ./gradlew jar --no-daemon mkdir -p ${CRAFT_PART_INSTALL}/jar From 7da96ac926ce228e12ba03558572fa7fdfff8278 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 19:20:58 +1200 Subject: [PATCH 37/46] fix(spring-boot): path to staging location --- rockcraft/extensions/spring_boot.py | 8 ++++---- tests/unit/extensions/test_spring_boot.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 1d9ee3ca9..55e6362ef 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -100,8 +100,8 @@ def gen_install_app_part(self) -> Dict[str, Any]: ], "override-build": """ mvn package - mkdir -p ${CRAFT_PART_INSTALL}/jar - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + mkdir -p ${CRAFT_PART_INSTALL}/jars + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, } @@ -118,8 +118,8 @@ def gen_install_app_part(self) -> Dict[str, Any]: ], "override-build": """ ./gradlew jar --no-daemon - mkdir -p ${CRAFT_PART_INSTALL}/jar - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + mkdir -p ${CRAFT_PART_INSTALL}/jars + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, } diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index ccb8ca0ce..ba4f90135 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -57,8 +57,8 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): ], "override-build": """ mvn package - mkdir -p ${CRAFT_PART_INSTALL}/jar - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + mkdir -p ${CRAFT_PART_INSTALL}/jars + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, }, @@ -109,8 +109,8 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): ], "override-build": """ ./gradlew jar --no-daemon - mkdir -p ${CRAFT_PART_INSTALL}/jar - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \\; + mkdir -p ${CRAFT_PART_INSTALL}/jars + find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, }, From 9c510acaa99b64848bc80ec28457aa2d4729b39d Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 30 Aug 2024 19:39:03 +1200 Subject: [PATCH 38/46] fix: add base-files_base to deps base-files_base are needed to allow Java runtime to access default directories. --- rockcraft/extensions/spring_boot.py | 1 + tests/unit/extensions/test_spring_boot.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 55e6362ef..825cace60 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -76,6 +76,7 @@ def get_runtime_deps_part(self) -> Dict[str, Any]: "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 \ diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index ba4f90135..4e9c8c3f4 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -76,6 +76,7 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "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 \ @@ -128,6 +129,7 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "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 \ From f749a2e88641f8160bdeef4bb32a7b28403ed948 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 15:11:20 +1200 Subject: [PATCH 39/46] fix(spring-boot, maven): only copy jars from target --- rockcraft/extensions/spring_boot.py | 2 +- tests/unit/extensions/test_spring_boot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 825cace60..23314f748 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -102,7 +102,7 @@ def gen_install_app_part(self) -> Dict[str, Any]: "override-build": """ mvn package mkdir -p ${CRAFT_PART_INSTALL}/jars - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; + find ${CRAFT_PART_BUILD}/target -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, } diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 4e9c8c3f4..6973d0d64 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -58,7 +58,7 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "override-build": """ mvn package mkdir -p ${CRAFT_PART_INSTALL}/jars - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; + find ${CRAFT_PART_BUILD}/target -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; craftctl default """, }, From 44da819971398c24d4f730971e3ab12738fe1371 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 15:12:06 +1200 Subject: [PATCH 40/46] fix(spring-boot, gradle): add stage and prime Gradle wrapper causes build script to abort. Add separate stage and prime to deploy the results. --- rockcraft/extensions/spring_boot.py | 16 ++++++++++------ tests/unit/extensions/test_spring_boot.py | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 23314f748..afd3623df 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -117,12 +117,16 @@ def gen_install_app_part(self) -> Dict[str, Any]: "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" } ], - "override-build": """ - ./gradlew jar --no-daemon - mkdir -p ${CRAFT_PART_INSTALL}/jars - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; - craftctl default - """, + "override-build": "./gradlew jar --no-daemon", + "override-stage": """ + mkdir -p ${CRAFT_STAGE}/jars + find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; + craftctl default + """, + "override-prime": """ + cp -r ${CRAFT_STAGE}/jars ${CRAFT_PRIME} + craftctl default + """, } return {} diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 6973d0d64..23ceae4d2 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -108,12 +108,16 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "build-environment": [ {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}"} ], - "override-build": """ - ./gradlew jar --no-daemon - mkdir -p ${CRAFT_PART_INSTALL}/jars - find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; - craftctl default - """, + "override-build": "./gradlew jar --no-daemon", + "override-stage": """ + mkdir -p ${CRAFT_STAGE}/jars + find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; + craftctl default + """, + "override-prime": """ + cp -r ${CRAFT_STAGE}/jars ${CRAFT_PRIME} + craftctl default + """, }, "spring-boot-framework/runtime": { "plugin": "jlink", From 01f1f68c218eace8878ea3b89ff55e5737af8ad7 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 15:31:36 +1200 Subject: [PATCH 41/46] lint(spring-boot): do not use deprecated variable --- rockcraft/extensions/spring_boot.py | 4 ++-- tests/unit/extensions/test_spring_boot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index afd3623df..9bbf1cbce 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -96,7 +96,7 @@ def gen_install_app_part(self) -> Dict[str, Any]: "build-packages": ["openjdk-21-jdk", "maven"], "build-environment": [ { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}" } ], "override-build": """ @@ -114,7 +114,7 @@ def gen_install_app_part(self) -> Dict[str, Any]: "build-packages": ["openjdk-21-jdk"], "build-environment": [ { - "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}" + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}" } ], "override-build": "./gradlew jar --no-daemon", diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 23ceae4d2..061331fd8 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -53,7 +53,7 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], "build-environment": [ - {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}"} + {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"} ], "override-build": """ mvn package @@ -106,7 +106,7 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "source-type": "local", "build-packages": ["openjdk-21-jdk"], "build-environment": [ - {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_TARGET_ARCH}"} + {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"} ], "override-build": "./gradlew jar --no-daemon", "override-stage": """ From c6d725d86d7af7b7b24f36ebc609fdb6d241b9cd Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 15:45:12 +1200 Subject: [PATCH 42/46] fix(spring-boot, gradle): use stage directory --- rockcraft/extensions/spring_boot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 9bbf1cbce..b45edfcea 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -120,7 +120,7 @@ def gen_install_app_part(self) -> Dict[str, Any]: "override-build": "./gradlew jar --no-daemon", "override-stage": """ mkdir -p ${CRAFT_STAGE}/jars - find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jars \\; + find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_STAGE}/jars \\; craftctl default """, "override-prime": """ From be667bc2277d544cbaec223601e6e6f646d4555e Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 16:06:25 +1200 Subject: [PATCH 43/46] fix: test assertion --- tests/unit/extensions/test_spring_boot.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 061331fd8..6022ba359 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -53,7 +53,9 @@ def test_spring_boot_extension_maven(tmp_path, spring_boot_input_yaml): "source-type": "local", "build-packages": ["openjdk-21-jdk", "maven"], "build-environment": [ - {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"} + { + "JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}" + } ], "override-build": """ mvn package @@ -106,12 +108,14 @@ def test_spring_boot_extension_gradle(tmp_path, spring_boot_input_yaml): "source-type": "local", "build-packages": ["openjdk-21-jdk"], "build-environment": [ - {"JAVA_HOME": "/usr/lib/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}"} + { + "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_PART_INSTALL}/jars \\; + find ${CRAFT_PART_BUILD}/build -iname "*.jar" -exec ln {} ${CRAFT_STAGE}/jars \\; craftctl default """, "override-prime": """ From f33557a40cb5005d3f6a2b18cf7fca034979f33e Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Mon, 2 Sep 2024 16:20:44 +1200 Subject: [PATCH 44/46] lint: drop unused import --- tests/unit/extensions/test_spring_boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/extensions/test_spring_boot.py b/tests/unit/extensions/test_spring_boot.py index 6022ba359..ee72871bb 100644 --- a/tests/unit/extensions/test_spring_boot.py +++ b/tests/unit/extensions/test_spring_boot.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import textwrap import pytest from rockcraft import extensions From a64b33df2b94958f3de2d823d3a4867f9a1f7d2e Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 27 Sep 2024 16:49:03 +1200 Subject: [PATCH 45/46] fix: check that part is not overriden Check that part is not overriden in check_project() before checking for maven/gradle. --- rockcraft/extensions/spring_boot.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index b45edfcea..220f2d39d 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -30,14 +30,15 @@ class SpringBootFramework(Extension): def _check_project(self): """Ensure that either pom.xml or gradlew is present.""" - 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, - ) + 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: From f8d8016c8add7fc934f549c755a5bc147337cf71 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Fri, 27 Sep 2024 16:58:09 +1200 Subject: [PATCH 46/46] fix: do not return run-user --- rockcraft/extensions/spring_boot.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rockcraft/extensions/spring_boot.py b/rockcraft/extensions/spring_boot.py index 220f2d39d..7ebb1fe21 100644 --- a/rockcraft/extensions/spring_boot.py +++ b/rockcraft/extensions/spring_boot.py @@ -64,9 +64,7 @@ def get_part_snippet(self) -> Dict[str, Any]: def get_root_snippet(self) -> Dict[str, Any]: self._check_project() - return { - "run_user": "_daemon_", - } + return {} def get_runtime_deps_part(self) -> Dict[str, Any]: if "spring-boot-framework/runtime-deps" not in self.yaml_data.get("parts", {}):