diff --git a/.github/labeler.yml b/.github/labeler.yml index 0918f61..a1aafcf 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,4 +1,4 @@ # Add 'documentation' label to any changes within 'docs' folder or any subfolders documentation: -- changed-files: - - any-glob-to-any-file: docs/** + - changed-files: + - any-glob-to-any-file: docs/** diff --git a/README.md b/README.md index 06f382d..87ffc33 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ To use the plugin, apply the following two steps: } apply(plugin = "com.canonical.rockcraft") - ### 2. Configure ROCK container The plugin allows setting up container summary and description, @@ -102,4 +101,3 @@ Contributions can be submitted via [Pull requests](https://github.com/canonical/ - Allow custom rockcraft.yaml/snippets - Error handling (empty jar file), no main class - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9dd0ea..9a96ce8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,6 @@ snakeyaml = "2.3" [libraries] junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } -commons-text = { module="org.apache.commons:commons-text", version.ref = "commons-text" } -osdetector = { module ="com.google.gradle:osdetector-gradle-plugin", version.ref = "osdetector" } +commons-text = { module = "org.apache.commons:commons-text", version.ref = "commons-text" } +osdetector = { module = "com.google.gradle:osdetector-gradle-plugin", version.ref = "osdetector" } snakeyaml = { module = "org.yaml:snakeyaml", version.ref = "snakeyaml" } diff --git a/rockcraft-plugin/build.gradle.kts b/rockcraft-gradle/build.gradle.kts similarity index 78% rename from rockcraft-plugin/build.gradle.kts rename to rockcraft-gradle/build.gradle.kts index 4c0f280..26ce1e5 100644 --- a/rockcraft-plugin/build.gradle.kts +++ b/rockcraft-gradle/build.gradle.kts @@ -12,12 +12,12 @@ repositories { } dependencies { + implementation(project(":rockcraft")) implementation(libs.osdetector) - implementation(libs.commons.text) - implementation(libs.snakeyaml) // Use JUnit Jupiter for testing. testImplementation(libs.junit.jupiter) + testImplementation(libs.snakeyaml) testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") @@ -32,7 +32,7 @@ gradlePlugin { id = "com.canonical.rockcraft" displayName = "Rockcraft plugin" description = "Plugin for rock image generation" - implementationClass = "com.canonical.rockcraft.plugin.RockcraftPlugin" + implementationClass = "com.canonical.rockcraft.gradle.RockcraftPlugin" tags = listOf("rockcraft", "rock", "container", "docker", "oci") } } @@ -42,3 +42,8 @@ tasks.named("test") { // Use JUnit Jupiter for unit tests. useJUnitPlatform() } + +tasks.withType { + val compilerArgs = options.compilerArgs + compilerArgs.add("-Xlint:all") +} diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/BuildRockcraftTask.java b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/BuildRockcraftTask.java similarity index 55% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/BuildRockcraftTask.java rename to rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/BuildRockcraftTask.java index 60d1415..3f92a90 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/BuildRockcraftTask.java +++ b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/BuildRockcraftTask.java @@ -3,7 +3,7 @@ * 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 @@ -11,13 +11,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.gradle; +import com.canonical.rockcraft.builder.RockBuilder; +import com.canonical.rockcraft.builder.RockProjectSettings; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.TaskAction; import java.io.IOException; -import java.nio.file.Files; /** * This task builds a ROCK image by calling rockcraft pack. @@ -25,12 +26,13 @@ */ public class BuildRockcraftTask extends DefaultTask { - private static final String ROCK_DIR = "rock"; - /** * Constructs BuildRockcraft task */ - public BuildRockcraftTask() { super();} + public BuildRockcraftTask() { + super(); + } + /** * The task action * @throws IOException - IO error while writing rockcraft.yaml @@ -38,25 +40,8 @@ public class BuildRockcraftTask extends DefaultTask { */ @TaskAction public void packRock() throws IOException, InterruptedException { - var buildDir = getProject().getLayout().getBuildDirectory(); - var pb = new ProcessBuilder("rockcraft", "pack") - .directory(buildDir.getAsFile().get()) - .inheritIO(); - var process = pb.start(); - int result = process.waitFor(); - if (result != 0) - throw new UnsupportedOperationException("Failed to pack rock for "+ getProject().getName()); - - var rockDest = buildDir.dir(ROCK_DIR).get().getAsFile(); - rockDest.mkdirs(); - for (var f : rockDest.listFiles((dir,file) -> file.endsWith(".rock"))) { - f.delete(); - } - // refresh rocks - for (var f : buildDir.getAsFile().get().listFiles((dir,file) -> file.endsWith(".rock"))) { - var source = f.toPath(); - var dest = buildDir.dir(ROCK_DIR).get().getAsFile().toPath(); - Files.move(source, dest.resolve(source.getFileName())); - } + var buildDir = getProject().getLayout().getBuildDirectory().getAsFile().get(); + var settings = new RockProjectSettings(getProject().getName(), String.valueOf(getProject().getVersion()), getProject().getProjectDir().toPath()); + RockBuilder.buildRock(settings, buildDir); } } diff --git a/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/CreateRockcraftTask.java b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/CreateRockcraftTask.java new file mode 100644 index 0000000..282f3fa --- /dev/null +++ b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/CreateRockcraftTask.java @@ -0,0 +1,67 @@ +/** + * 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 . + */ +package com.canonical.rockcraft.gradle; + +import com.canonical.rockcraft.builder.RockCrafter; +import com.canonical.rockcraft.builder.RockProjectSettings; +import com.canonical.rockcraft.builder.RockcraftOptions; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; + +/** + * This task writes rockcraft.yaml file for the application. + */ +public abstract class CreateRockcraftTask extends DefaultTask { + + private final RockcraftOptions options; + + /** + * Constructs the CreateRockcraft task + * @param options - plugin options + */ + @Inject + public CreateRockcraftTask(RockcraftOptions options) { + this.options = options; + } + + private RockcraftOptions getOptions() { + return options; + } + + /** + * Task action to write rockcraft.yaml + */ + @TaskAction + public void writeRockcraft() { + HashSet artifacts = new HashSet(); + for (var conf : getProject().getConfigurations()) { + artifacts.addAll(conf.getArtifacts().getFiles().getFiles().stream().filter(x -> x.getName().endsWith("jar")).toList()); + } + + try { + var buildDir = getProject().getLayout().getBuildDirectory(); + var settings = new RockProjectSettings(getProject().getName(), String.valueOf(getProject().getVersion()), getProject().getProjectDir().toPath()); + RockCrafter crafter = new RockCrafter(settings, getOptions(), buildDir.getAsFile().get(), new ArrayList(artifacts)); + crafter.writeRockcraft(); + } catch (IOException e) { + throw new UnsupportedOperationException("Failed to write rockcraft.yaml: " + e.getMessage()); + } + } +} diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftPlugin.java b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/RockcraftPlugin.java similarity index 82% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftPlugin.java rename to rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/RockcraftPlugin.java index b12de43..fda90e9 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftPlugin.java +++ b/rockcraft-gradle/src/main/java/com/canonical/rockcraft/gradle/RockcraftPlugin.java @@ -3,7 +3,7 @@ * 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 @@ -11,8 +11,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.gradle; +import com.canonical.rockcraft.builder.RockBuilder; +import com.canonical.rockcraft.builder.RockcraftOptions; import com.google.gradle.osdetector.OsDetector; import com.google.gradle.osdetector.OsDetectorPlugin; import org.gradle.api.Plugin; @@ -29,7 +31,9 @@ public class RockcraftPlugin implements Plugin { /** * Constructs RockcraftPlugin */ - public RockcraftPlugin() { super(); } + public RockcraftPlugin() { + super(); + } /** @@ -48,16 +52,10 @@ public void apply(Project project) { throw new UnsupportedOperationException("Rockcraft is only supported on linux systems"); var checkTask = project.getTasks().register("checkRockcraft", s -> { - s.doFirst( x -> { + s.doFirst(x -> { try { - var pb = new ProcessBuilder("rockcraft", "--version"); - pb.inheritIO(); - var versionProcess = pb.start(); - int ret = versionProcess.waitFor(); - if (ret != 0) - throw new UnsupportedOperationException("Please install rockcraft 'snap install rockcraft'."); - } - catch (IOException | InterruptedException e) { + RockBuilder.checkRockcraft(); + } catch (IOException | InterruptedException e) { throw new UnsupportedOperationException(e.getMessage()); } }); diff --git a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/BaseRockcraftTest.java b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/BaseRockcraftTest.java similarity index 95% rename from rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/BaseRockcraftTest.java rename to rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/BaseRockcraftTest.java index 91b56a9..7ac185b 100644 --- a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/BaseRockcraftTest.java +++ b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/BaseRockcraftTest.java @@ -3,7 +3,7 @@ * 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 @@ -11,14 +11,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.gradle; +import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; -import org.gradle.testkit.runner.BuildResult; - import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -36,7 +35,9 @@ protected File getJavaSource() { return Path.of(projectDir.getAbsolutePath(), "src", "main", "java", "Test.java").toFile(); } - protected File getProjectDir() { return projectDir; } + protected File getProjectDir() { + return projectDir; + } protected File getBuildFile() { return new File(projectDir, "build.gradle"); diff --git a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RockcraftPluginTest.java b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/RockcraftPluginTest.java similarity index 69% rename from rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RockcraftPluginTest.java rename to rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/RockcraftPluginTest.java index 7037505..4c0fc11 100644 --- a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RockcraftPluginTest.java +++ b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/RockcraftPluginTest.java @@ -3,7 +3,7 @@ * 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 @@ -11,7 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.gradle; import org.gradle.testkit.runner.TaskOutcome; import org.junit.jupiter.api.Test; @@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@SuppressWarnings("unchecked") class RockcraftPluginTest extends BaseRockcraftTest { @Test @@ -39,15 +40,15 @@ void validRockcraftYaml() throws IOException { runBuild("create-rock"); try (var is = new FileInputStream(Path.of(getProjectDir().getAbsolutePath(), "build", "rockcraft.yaml").toFile())) { var yaml = new Yaml(); - Map parsed = yaml.load(is); + Map> parsed = yaml.load(is); assertEquals("ubuntu@24.04", parsed.get("build-base")); - Map parts = (Map)parsed.get("parts"); + Map parts = parsed.get("parts"); // - Map dumpPart = (Map)parts.get("gradle/rockcraft/dump"); + Map dumpPart = (Map) parts.get("gradle/rockcraft/dump"); assertTrue(dumpPart.containsKey("override-build")); - Map runtimePart = (Map)parts.get("gradle/rockcraft/runtime"); + Map runtimePart = (Map) parts.get("gradle/rockcraft/runtime"); assertTrue(runtimePart.containsKey("override-build")); - Map depsPart = (Map)parts.get("gradle/rockcraft/deps"); + Map depsPart = (Map) parts.get("gradle/rockcraft/deps"); assertTrue(depsPart.containsKey("override-build")); } } @@ -59,12 +60,12 @@ void buildRockJava11Test() throws IOException { id('java') id('com.canonical.rockcraft') } - + rockcraft { buildPackage = "openjdk-11-jdk" targetRelease = 11 } - + """); var result = runBuild("build-rock"); assertTrue(true); // the build needs to succeed @@ -77,12 +78,12 @@ void rockcraftPluginOptions() throws IOException { id('java') id('com.canonical.rockcraft') } - + rockcraft { summary = "Foobar" description = "readme.txt" } - + """); writeString(new File(getProjectDir(), "readme.txt"), """ This is a multiline description @@ -103,19 +104,19 @@ void testArchitecture() throws IOException { id('java') id('com.canonical.rockcraft') } - + rockcraft { summary = "Foobar" architectures = [ "amd64", "arm64" ] } - + """); runBuild("jar", "create-rock"); try (var is = new FileInputStream(Path.of(getProjectDir().getAbsolutePath(), "build", "rockcraft.yaml").toFile())) { var yaml = new Yaml(); - Map parsed = yaml.load(is); - Map platforms = (Map)parsed.get("platforms"); + Map> parsed = yaml.load(is); + Map platforms = parsed.get("platforms"); assertTrue(platforms.containsKey("amd64")); assertTrue(platforms.containsKey("arm64")); } @@ -124,35 +125,35 @@ void testArchitecture() throws IOException { @Test void onlySingleRockExists() throws IOException { writeString(getBuildFile(), """ - plugins { - id('java') - id('com.canonical.rockcraft') - } - - version = 0.01 - - rockcraft { - } - - """); + plugins { + id('java') + id('com.canonical.rockcraft') + } + + version = 0.01 + + rockcraft { + } + + """); runBuild("build-rock"); - File output = Path.of(getProjectDir().getAbsolutePath(), "build","rock").toFile(); - assertEquals(1, output.list( (dir, name) -> name.endsWith("rock")).length); + File output = Path.of(getProjectDir().getAbsolutePath(), "build", "rock").toFile(); + assertEquals(1, output.list((dir, name) -> name.endsWith("rock")).length); writeString(getBuildFile(), """ - plugins { - id('java') - id('com.canonical.rockcraft') - } - - version = '0.02updated' - - rockcraft { - } - - """); + plugins { + id('java') + id('com.canonical.rockcraft') + } + + version = '0.02updated' + + rockcraft { + } + + """); runBuild("build-rock"); - String[] rocks = output.list( (dir, name) -> name.endsWith("rock")); + String[] rocks = output.list((dir, name) -> name.endsWith("rock")); assertEquals(1, rocks.length); assertTrue(rocks[0].contains("0.02updated")); } @@ -161,25 +162,25 @@ void onlySingleRockExists() throws IOException { void testAllOptions() throws IOException { writeString(new File(getProjectDir(), "README.md"), "test"); writeString(getBuildFile(), """ - plugins { - id('java') - id('com.canonical.rockcraft') - } - - version = 0.01 - - rockcraft { - buildPackage = 'openjdk-17-jdk' - targetRelease = 17 - summary = 'A ROCK summary' - description = 'README.md' - command = '/usr/bin/java -jar jars/application.jar' - source = 'http://github.com/canonical/chisel-releases' - branch = 'ubuntu-24.04' - slices = ['busybox_bins', 'ca-certificates_data-with-certs'] - architectures = ['amd64', 'arm64'] - } - """); + plugins { + id('java') + id('com.canonical.rockcraft') + } + + version = 0.01 + + rockcraft { + buildPackage = 'openjdk-17-jdk' + targetRelease = 17 + summary = 'A ROCK summary' + description = 'README.md' + command = '/usr/bin/java -jar jars/application.jar' + source = 'http://github.com/canonical/chisel-releases' + branch = 'ubuntu-24.04' + slices = ['busybox_bins', 'ca-certificates_data-with-certs'] + architectures = ['amd64', 'arm64'] + } + """); var result = runBuild("build-rock", "--stacktrace"); assertEquals(TaskOutcome.SUCCESS, result.getTasks().getLast().getOutcome()); // the build needs to succeed } diff --git a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/SpringBootTest.java b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/SpringBootTest.java similarity index 94% rename from rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/SpringBootTest.java rename to rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/SpringBootTest.java index 6a9bf6a..17dc989 100644 --- a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/SpringBootTest.java +++ b/rockcraft-gradle/src/test/java/com/canonical/rockcraft/gradle/SpringBootTest.java @@ -3,7 +3,7 @@ * 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 @@ -11,15 +11,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; - -import java.io.IOException; -import java.nio.file.Path; +package com.canonical.rockcraft.gradle; import org.gradle.testkit.runner.TaskOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.nio.file.Path; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -32,17 +32,17 @@ public class SpringBootTest extends BaseRockcraftTest { protected void setUp() throws IOException { String app = """ package com.example.app2; - + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; - + @SpringBootApplication public class DemoApplication { - + public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } - + } """; Path javaDir = Path.of(projectDir.getAbsolutePath(), "src", "main", "java", "com", "example", "app2"); @@ -55,14 +55,14 @@ public static void main(String[] args) { id 'io.spring.dependency-management' version '1.1.6' id 'com.canonical.rockcraft' } - + group = 'com.example' version = '0.0.1-SNAPSHOT' - + repositories { mavenCentral() } - + dependencies { implementation 'org.springframework.boot:spring-boot-starter' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/rockcraft/build.gradle.kts b/rockcraft/build.gradle.kts new file mode 100644 index 0000000..d2a6cbd --- /dev/null +++ b/rockcraft/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `java` +} + +group = "com.canonical" +version = "0.1.1" + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + implementation(libs.snakeyaml) + + // Use JUnit Jupiter for testing. + testImplementation(libs.junit.jupiter) + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") +} + +tasks.named("test") { + // Use JUnit Jupiter for unit tests. + useJUnitPlatform() +} diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/IRuntimeProvider.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/IRuntimeProvider.java similarity index 95% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/IRuntimeProvider.java rename to rockcraft/src/main/java/com/canonical/rockcraft/builder/IRuntimeProvider.java index 869780a..09ccc51 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/IRuntimeProvider.java +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/IRuntimeProvider.java @@ -3,7 +3,7 @@ * 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 @@ -11,7 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; import java.io.File; import java.util.List; @@ -26,6 +26,7 @@ public interface IRuntimeProvider { /** * Generate rockcraft Java runtime part code. + * * @param files - list of jar files to analyze * @return part code */ diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/JLinkRuntimePart.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/JLinkRuntimePart.java similarity index 94% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/JLinkRuntimePart.java rename to rockcraft/src/main/java/com/canonical/rockcraft/builder/JLinkRuntimePart.java index 33f148b..544fe90 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/JLinkRuntimePart.java +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/JLinkRuntimePart.java @@ -3,7 +3,7 @@ * 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 @@ -11,7 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; import java.io.File; import java.util.ArrayList; @@ -30,7 +30,7 @@ public class JLinkRuntimePart implements IRuntimeProvider { * Constructs the JLinkRuntimePart * @param options - plugin options */ - public JLinkRuntimePart(RockcraftOptions options){ + public JLinkRuntimePart(RockcraftOptions options) { this.options = options; } diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RawRuntimePart.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RawRuntimePart.java similarity index 96% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RawRuntimePart.java rename to rockcraft/src/main/java/com/canonical/rockcraft/builder/RawRuntimePart.java index d11866d..f4ee445 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RawRuntimePart.java +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RawRuntimePart.java @@ -3,7 +3,7 @@ * 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 @@ -11,7 +11,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; import java.io.File; import java.util.HashMap; @@ -59,7 +59,7 @@ public Map getRuntimePart(List files) { var commands = new StringBuffer(); append(commands, "JAVA_HOME=$(dirname $(dirname $(readlink -f /usr/bin/java)))"); append(commands, "JAVA_HOME=${JAVA_HOME:1}"); - append(commands, "PROCESS_JARS=\"" + jarList+"\""); + append(commands, "PROCESS_JARS=\"" + jarList + "\""); append(commands, "mkdir -p ${CRAFT_PART_BUILD}/tmp"); append(commands, "(cd ${CRAFT_PART_BUILD}/tmp && for jar in ${PROCESS_JARS}; do jar xvf ${jar}; done;)" diff --git a/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockBuilder.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockBuilder.java new file mode 100644 index 0000000..5ab1196 --- /dev/null +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockBuilder.java @@ -0,0 +1,76 @@ +/** + * 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 . + */ +package com.canonical.rockcraft.builder; + + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +/** + * Utilities to build rock image + */ +public class RockBuilder { + + private static final String ROCK_DIR = "rock"; + + /** + * Creates RockBuilder + */ + public RockBuilder() { + } + + /** + * Checks that rockcraft is installed + * @throws InterruptedException - unable to start rockcraft + * @throws IOException - unable to start rockcraft + */ + public static void checkRockcraft() throws InterruptedException, IOException { + var pb = new ProcessBuilder("rockcraft", "--version"); + pb.inheritIO(); + var versionProcess = pb.start(); + int ret = versionProcess.waitFor(); + if (ret != 0) + throw new UnsupportedOperationException("Please install rockcraft 'snap install rockcraft'."); + } + + /** + * Builds the rock image + * @param settings - rockcraft project settings + * @param output - output directory + * @throws IOException - IO error while writing rockcraft.yaml + * @throws InterruptedException - rockcraft process was aborted + */ + public static void buildRock(RockProjectSettings settings, File output) throws InterruptedException, IOException { + var pb = new ProcessBuilder("rockcraft", "pack") + .directory(output) + .inheritIO(); + var process = pb.start(); + int result = process.waitFor(); + if (result != 0) + throw new UnsupportedOperationException("Failed to pack rock for " + settings.getName()); + + var rockDest = new File(output, ROCK_DIR); + rockDest.mkdirs(); + for (var f : rockDest.listFiles((dir, file) -> file.endsWith(".rock"))) { + f.delete(); + } + // refresh rocks + for (var f : output.listFiles((dir, file) -> file.endsWith(".rock"))) { + var source = f.toPath(); + var dest = new File(output, ROCK_DIR).toPath(); + Files.move(source, dest.resolve(source.getFileName())); + } + } +} diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/CreateRockcraftTask.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockCrafter.java similarity index 76% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/CreateRockcraftTask.java rename to rockcraft/src/main/java/com/canonical/rockcraft/builder/RockCrafter.java index b3c9add..e8014de 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/CreateRockcraftTask.java +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockCrafter.java @@ -3,7 +3,7 @@ * 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 @@ -11,56 +11,55 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.TaskAction; import org.yaml.snakeyaml.Yaml; -import javax.inject.Inject; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * This task writes rockcraft.yaml file for the application. + * Creates a rockcraft.yaml based on RockOptions */ -public abstract class CreateRockcraftTask extends DefaultTask { +public class RockCrafter { private static final String ROCKCRAFT_YAML = "rockcraft.yaml"; + private final RockProjectSettings settings; private final RockcraftOptions options; - - private RockcraftOptions getOptions() { - return options; - } + private final File output; + private final List artifacts; /** - * Constructs the CreateRockcraft task - * @param options - plugin options + * Creates RockCrafter + * @param settings - Rockcraft project settins + * @param options - Rockcraft creation options + * @param output - output directory for rockcraft.yaml + * @param artifacts - list of artifacts to package */ - @Inject - public CreateRockcraftTask(RockcraftOptions options) { + public RockCrafter(RockProjectSettings settings, RockcraftOptions options, File output, List artifacts) { + this.settings = settings; this.options = options; + this.output = output; + this.artifacts = artifacts; } /** - * Task action to write rockcraft.yaml + * Writes a rockcraft.yaml file to the output directory + * @throws IOException - the method fails to write rockcraft.yaml */ - @TaskAction - public void writeRockcraft() { - HashSet artifacts = new HashSet(); - for (var conf : getProject().getConfigurations()) { - artifacts.addAll(conf.getArtifacts().getFiles().getFiles().stream().filter(x -> x.getName().endsWith("jar")).toList()); - } - try { - var buildDir = getProject().getLayout().getBuildDirectory(); - try (BufferedWriter wr = new BufferedWriter(new FileWriter(buildDir.file(ROCKCRAFT_YAML).get().getAsFile()))) { - wr.write(createRockcraft(buildDir.getAsFile().get().toPath(), artifacts.stream().toList())); - } - } catch (IOException e) { - throw new UnsupportedOperationException("Failed to write rockcraft.yaml: " + e.getMessage()); + public void writeRockcraft() throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(output, ROCKCRAFT_YAML)))) { + String rockcraft = createRockcraft(output.toPath(), artifacts); + writer.write(rockcraft); } } @@ -79,12 +78,12 @@ protected String createRockcraft(Path root, List files) throws IOException relativeJars.add(root.relativize(file.toPath()).toString()); var rockcraft = new HashMap(); - rockcraft.put("name", getProject().getName()); - rockcraft.put("version", String.valueOf(getProject().getVersion())); + rockcraft.put("name", settings.getName()); + rockcraft.put("version", String.valueOf(settings.getVersion())); rockcraft.put("summary", getOptions().getSummary()); var description = getOptions().getDescription(); if (description != null) { - var descriptionFile = Path.of(getProject().getProjectDir().getAbsolutePath(), description.toString()).toFile(); + var descriptionFile = settings.getProjectPath().resolve(description).toFile(); if (!descriptionFile.exists()) throw new UnsupportedOperationException("Rockcraft plugin description file does not exist."); rockcraft.put("description", new String(Files.readAllBytes(descriptionFile.toPath()))); @@ -113,7 +112,7 @@ private Map getPlatforms() { * Return list of the chisel slices */ private String getProjectDeps() { - var buffer = new StringBuffer(); + var buffer = new StringBuilder(); for (var dep : getOptions().getSlices()) { if (!buffer.isEmpty()) buffer.append(" "); @@ -130,7 +129,7 @@ private String getProjectDeps() { * @return */ private String getProjectCopyOutput(List relativeJars) { - var buffer = new StringBuffer(); + var buffer = new StringBuilder(); buffer.append("mkdir -p ${CRAFT_PART_INSTALL}/jars\n"); for (var jar : relativeJars) { buffer.append(String.format("cp %s ${CRAFT_PART_INSTALL}/jars\n", jar)); @@ -172,15 +171,15 @@ private Map getDepsPart() { overrideCommands += "--release ./ "; } overrideCommands += """ - --root ${CRAFT_PART_INSTALL}/ libc6_libs \\ - libgcc-s1_libs \\ - libstdc++6_libs \\ - zlib1g_libs \\ - base-files_base \\ - libnss3_libs """; + --root ${CRAFT_PART_INSTALL}/ libc6_libs \\ + libgcc-s1_libs \\ + libstdc++6_libs \\ + zlib1g_libs \\ + base-files_base \\ + libnss3_libs """; if (getProjectDeps() != null) { - overrideCommands +=" "+ getProjectDeps(); + overrideCommands += " " + getProjectDeps(); } overrideCommands += "\ncraftctl default\n"; part.put("override-build", overrideCommands); @@ -201,7 +200,7 @@ private Map getProjectService(List relativeJars) { message.append(" "); } message.append("]"); - throw new UnsupportedOperationException("Rockcraft plugin requires either single jar output or a command defined: "+ message); + throw new UnsupportedOperationException("Rockcraft plugin requires either single jar output or a command defined: " + message); } } var serviceData = new HashMap(); @@ -210,7 +209,11 @@ private Map getProjectService(List relativeJars) { serviceData.put("startup", "enabled"); serviceData.put("command", command); var services = new HashMap(); - services.put(getProject().getName(), serviceData); + services.put(settings.getName(), serviceData); return services; } + + private RockcraftOptions getOptions() { + return options; + } } diff --git a/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockProjectSettings.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockProjectSettings.java new file mode 100644 index 0000000..7da8131 --- /dev/null +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockProjectSettings.java @@ -0,0 +1,52 @@ +package com.canonical.rockcraft.builder; + +import java.nio.file.Path; + +/** + * Store project settings for the project + */ +public class RockProjectSettings { + private final String name; + private final String version; + private final Path projectPath; + + /** + * Constructs the rock project settings + * + * @param name rockcraft project name + * @param version rockcraft project version + * @param path path to the rockcraft project + */ + public RockProjectSettings(String name, String version, Path path) { + this.name = name; + this.version = version; + this.projectPath = path; + } + + /** + * Get the project name + * + * @return project name + */ + public String getName() { + return name; + } + + /** + * Get the project version + * + * @return the project version + */ + public String getVersion() { + return version; + } + + /** + * Get the project root path + * + * @return project root path + */ + public Path getProjectPath() { + return projectPath; + } +} diff --git a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftOptions.java b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockcraftOptions.java similarity index 97% rename from rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftOptions.java rename to rockcraft/src/main/java/com/canonical/rockcraft/builder/RockcraftOptions.java index 813d5f7..6db5800 100644 --- a/rockcraft-plugin/src/main/java/com/canonical/rockcraft/plugin/RockcraftOptions.java +++ b/rockcraft/src/main/java/com/canonical/rockcraft/builder/RockcraftOptions.java @@ -3,7 +3,7 @@ * 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 @@ -11,55 +11,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; /** - * Rockcraft plugin options + * Rockcraft creation options */ public class RockcraftOptions { - /** - * Construct RockcraftOptions - */ - public RockcraftOptions(){} - /** - * The list of supported Ubuntu architectures - */ - public enum RockArchitecture { - /** - * AMD64 - */ - amd64, - /** - * ARM64 - */ - arm64, - /** - * ARM hard float - */ - armhf, - /** - * I386 - */ - i386, - /** - * PowerPC 64 EL - */ - ppc64el, - /** - * RISCV - */ - riscv64, - /** - * S390X - */ - s390x; - } - private String buildPackage = "openjdk-21-jdk"; private int targetRelease = 21; private boolean jlink = false; @@ -70,6 +32,11 @@ public enum RockArchitecture { private String branch; private RockArchitecture[] architectures = new RockArchitecture[0]; private List slices = new ArrayList(); + /** + * Construct RockcraftOptions + */ + public RockcraftOptions() { + } /** * Gets the target release (integer) @@ -231,4 +198,38 @@ public String getSource() { public void setSource(String source) { this.source = source; } + + /** + * The list of supported Ubuntu architectures + */ + public enum RockArchitecture { + /** + * AMD64 + */ + amd64, + /** + * ARM64 + */ + arm64, + /** + * ARM hard float + */ + armhf, + /** + * I386 + */ + i386, + /** + * PowerPC 64 EL + */ + ppc64el, + /** + * RISCV + */ + riscv64, + /** + * S390X + */ + s390x + } } diff --git a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RawRunimePartTest.java b/rockcraft/src/test/java/com/canonical/rockcraft/builder/RawRunimePartTest.java similarity index 89% rename from rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RawRunimePartTest.java rename to rockcraft/src/test/java/com/canonical/rockcraft/builder/RawRunimePartTest.java index 06109b4..41d4fa1 100644 --- a/rockcraft-plugin/src/test/java/com/canonical/rockcraft/plugin/RawRunimePartTest.java +++ b/rockcraft/src/test/java/com/canonical/rockcraft/builder/RawRunimePartTest.java @@ -3,7 +3,7 @@ * 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 @@ -11,24 +11,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.canonical.rockcraft.plugin; +package com.canonical.rockcraft.builder; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.Arrays; import java.util.Map; -public class RawRunimePartTest extends BaseRockcraftTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RawRunimePartTest { @Test void rawRuntimePart() { RockcraftOptions options = new RockcraftOptions(); RawRuntimePart part = new RawRuntimePart(options); Map code = part.getRuntimePart(Arrays.stream(new File[]{new File("/tmpfoo.jar")}).toList()); assertEquals("nil", code.get("plugin")); - var ret = (String[])code.get("build-packages"); + var ret = (String[]) code.get("build-packages"); assertEquals("openjdk-21-jdk", ret[0]); assertTrue(code.get("override-build").toString().contains("--multi-release 21")); } @@ -40,7 +41,7 @@ void rawRuntimeCustomOpenjdk() { options.setBuildPackage("openjdk-11-jdk"); RawRuntimePart part = new RawRuntimePart(options); Map code = part.getRuntimePart(Arrays.stream(new File[]{new File("/tmpfoo.jar")}).toList()); - var ret = (String[])code.get("build-packages"); + var ret = (String[]) code.get("build-packages"); assertEquals("openjdk-11-jdk", ret[0]); assertTrue(code.get("override-build").toString().contains("--multi-release 8")); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 63b538f..af7a039 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,4 +5,6 @@ pluginManagement { } rootProject.name = "rockcraft-plugin" -include("rockcraft-plugin") + +include("rockcraft", "rockcraft-gradle") +