| @@ -1,57 +1,65 | |||||
| 1 | plugins { |
|
1 | plugins { | |
| 2 | id "java-gradle-plugin" |
|
2 | id "java-gradle-plugin" | |
| 3 | id "com.gradle.plugin-publish" version "1.2.1" |
|
3 | id "com.gradle.plugin-publish" version "1.2.1" | |
| 4 | // Maven Publish Plugin applied by com.gradle.plugin-publish > 1.0 |
|
4 | // Maven Publish Plugin applied by com.gradle.plugin-publish > 1.0 | |
| 5 | id "ivy-publish" |
|
5 | id "ivy-publish" | |
| 6 | } |
|
6 | } | |
| 7 |
|
7 | |||
| 8 | dependencies { |
|
8 | dependencies { | |
| 9 | implementation "com.fasterxml.jackson.core:jackson-core:2.13.5", |
|
9 | implementation "com.fasterxml.jackson.core:jackson-core:2.13.5", | |
| 10 | "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5" |
|
10 | "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5" | |
| 11 | } |
|
11 | } | |
| 12 |
|
12 | |||
| 13 | java { |
|
13 | java { | |
| 14 | targetCompatibility = 11 |
|
14 | targetCompatibility = 11 | |
| 15 | sourceCompatibility = 11 |
|
15 | sourceCompatibility = 11 | |
| 16 | } |
|
16 | } | |
| 17 |
|
17 | |||
| 18 |
|
18 | |||
| 19 | repositories { |
|
19 | repositories { | |
| 20 | mavenCentral() |
|
20 | mavenCentral() | |
| 21 | } |
|
21 | } | |
| 22 |
|
22 | |||
| 23 | gradlePlugin { |
|
23 | gradlePlugin { | |
| 24 | website = 'https://code.implab.org/implab/gradle-container-plugin' |
|
24 | website = 'https://code.implab.org/implab/gradle-container-plugin' | |
| 25 | vcsUrl = 'https://code.implab.org/implab/gradle-container-plugin' |
|
25 | vcsUrl = 'https://code.implab.org/implab/gradle-container-plugin' | |
| 26 | plugins { |
|
26 | plugins { | |
| 27 | containerPlugin { |
|
27 | containerPlugin { | |
| 28 | id = 'org.implab.gradle-container' |
|
28 | id = 'org.implab.gradle-container' | |
| 29 | displayName = "Provdes convetional configuration to build a container image" |
|
29 | displayName = "Provdes convetional configuration to build a container image" | |
| 30 | description = 'Build and publish container images with docker or podman. Simple wrapper around cli.' |
|
30 | description = 'Build and publish container images with docker or podman. Simple wrapper around cli.' | |
| 31 | implementationClass = 'org.implab.gradle.containers.ContainerPlugin' |
|
31 | implementationClass = 'org.implab.gradle.containers.ContainerPlugin' | |
| 32 | tags.set(['containers', 'image', 'docker', 'podman']) |
|
32 | tags.set(['containers', 'image', 'docker', 'podman']) | |
| 33 | } |
|
33 | } | |
| 34 | containerBasePlugin { |
|
34 | containerBasePlugin { | |
| 35 | id = 'org.implab.gradle-container-base' |
|
35 | id = 'org.implab.gradle-container-base' | |
| 36 | displayName = "Provides tasks to build manipulate container images" |
|
36 | displayName = "Provides tasks to build manipulate container images" | |
| 37 | description = 'Build and publish container images with docker or podman. Simple wrapper around cli.' |
|
37 | description = 'Build and publish container images with docker or podman. Simple wrapper around cli.' | |
| 38 | implementationClass = 'org.implab.gradle.containers.ContainerBasePlugin' |
|
38 | implementationClass = 'org.implab.gradle.containers.ContainerBasePlugin' | |
| 39 | tags.set(['containers', 'image', 'docker', 'podman']) |
|
39 | tags.set(['containers', 'image', 'docker', 'podman']) | |
| 40 | } |
|
40 | } | |
|
|
41 | ||||
|
|
42 | conmposePlugin { | |||
|
|
43 | id = 'org.implab.gradle-container-compose' | |||
|
|
44 | displayName = "Provdes tasks to start and stop compose" | |||
|
|
45 | description = 'Build and publish container images with docker or podman. Simple wrapper around cli.' | |||
|
|
46 | implementationClass = 'org.implab.gradle.containers.ComposePlugin' | |||
|
|
47 | tags.set(['containers', 'image', 'docker', 'podman']) | |||
|
|
48 | } | |||
| 41 | } |
|
49 | } | |
| 42 | } |
|
50 | } | |
| 43 |
|
51 | |||
| 44 |
|
52 | |||
| 45 | task printVersion { |
|
53 | task printVersion { | |
| 46 | doLast { |
|
54 | doLast { | |
| 47 | println "${->jar.archiveFileName.get()}" |
|
55 | println "${->jar.archiveFileName.get()}" | |
| 48 | } |
|
56 | } | |
| 49 | } |
|
57 | } | |
| 50 |
|
58 | |||
| 51 | publishing { |
|
59 | publishing { | |
| 52 | repositories { |
|
60 | repositories { | |
| 53 | ivy { |
|
61 | ivy { | |
| 54 | url "${System.properties["user.home"]}/ivy-repo" |
|
62 | url "${System.properties["user.home"]}/ivy-repo" | |
| 55 | } |
|
63 | } | |
| 56 | } |
|
64 | } | |
| 57 | } |
|
65 | } | |
| @@ -1,49 +1,53 | |||||
| 1 | package org.implab.gradle.containers; |
|
1 | package org.implab.gradle.containers; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Plugin; |
|
3 | import org.gradle.api.Plugin; | |
| 4 | import org.gradle.api.Project; |
|
4 | import org.gradle.api.Project; | |
| 5 | import org.gradle.api.plugins.ExtraPropertiesExtension; |
|
5 | import org.gradle.api.plugins.ExtraPropertiesExtension; | |
| 6 | import org.implab.gradle.containers.tasks.BuildImage; |
|
6 | import org.implab.gradle.containers.tasks.BuildImage; | |
| 7 | import org.implab.gradle.containers.tasks.PushImage; |
|
7 | import org.implab.gradle.containers.tasks.PushImage; | |
| 8 | import org.implab.gradle.containers.tasks.RunImage; |
|
8 | import org.implab.gradle.containers.tasks.RunImage; | |
| 9 | import org.implab.gradle.containers.tasks.SaveImage; |
|
9 | import org.implab.gradle.containers.tasks.SaveImage; | |
| 10 | import org.implab.gradle.containers.tasks.TagImage; |
|
10 | import org.implab.gradle.containers.tasks.TagImage; | |
| 11 |
|
11 | |||
| 12 |
public class ContainerBasePlugin implements Plugin<Project> |
|
12 | public class ContainerBasePlugin implements Plugin<Project> { | |
| 13 | public static final String CONTAINER_EXTENSION_NAME = "container"; |
|
13 | public static final String CONTAINER_EXTENSION_NAME = "container"; | |
| 14 |
|
14 | |||
| 15 | ContainerExtension containerExtension; |
|
15 | private ContainerExtension containerExtension; | |
| 16 |
|
16 | |||
| 17 | ContainerExtension getContainerExtension() { |
|
17 | ContainerExtension getContainerExtension() { | |
| 18 | if (containerExtension == null) |
|
18 | if (containerExtension == null) | |
| 19 | throw new IllegalStateException(); |
|
19 | throw new IllegalStateException(); | |
| 20 | return containerExtension; |
|
20 | return containerExtension; | |
| 21 | } |
|
21 | } | |
| 22 |
|
22 | |||
| 23 | void exportClasses(Project project, Class<?>... classes) { |
|
23 | void exportClasses(Project project, Class<?>... classes) { | |
| 24 | ExtraPropertiesExtension extras = project.getExtensions().getExtraProperties(); |
|
24 | ExtraPropertiesExtension extras = project.getExtensions().getExtraProperties(); | |
| 25 | for (var clazz : classes) |
|
25 | for (var clazz : classes) | |
| 26 | extras.set(clazz.getSimpleName(), clazz); |
|
26 | extras.set(clazz.getSimpleName(), clazz); | |
| 27 | } |
|
27 | } | |
| 28 |
|
28 | |||
| 29 | @Override |
|
29 | @Override | |
| 30 | public void apply(Project project) { |
|
30 | public void apply(Project project) { | |
| 31 |
|
31 | |||
| 32 | containerExtension = project.getObjects().newInstance(ContainerExtension.class); |
|
32 | containerExtension = project.getObjects().newInstance(ContainerExtension.class); | |
| 33 |
|
33 | |||
| 34 | containerExtension.getImageAuthority() |
|
34 | containerExtension.getImageAuthority() | |
| 35 | .convention(project.provider(() -> (String) project.getProperties().get("imagesAuthority"))); |
|
35 | .convention(project.provider(() -> (String) project.getProperties().get("imagesAuthority"))); | |
| 36 |
|
36 | |||
| 37 | containerExtension.getImageGroup() |
|
37 | containerExtension.getImageGroup() | |
| 38 | .convention(project.provider(() -> (String) project.getProperties().get("imagesGroup"))); |
|
38 | .convention(project.provider(() -> (String) project.getProperties().get("imagesGroup"))); | |
| 39 |
|
39 | |||
|
|
40 | containerExtension.getCliCmd() | |||
|
|
41 | .convention(project | |||
|
|
42 | .provider(() -> (String) project.getProperties().get("containerCli")) | |||
|
|
43 | .orElse("docker")); | |||
|
|
44 | ||||
| 40 | project.getExtensions().add(CONTAINER_EXTENSION_NAME, containerExtension); |
|
45 | project.getExtensions().add(CONTAINER_EXTENSION_NAME, containerExtension); | |
| 41 |
|
46 | |||
| 42 | exportClasses( |
|
47 | exportClasses( | |
| 43 | project, |
|
48 | project, | |
| 44 | BuildImage.class, PushImage.class, SaveImage.class, TagImage.class, RunImage.class); |
|
49 | BuildImage.class, PushImage.class, SaveImage.class, TagImage.class, RunImage.class); | |
| 45 |
|
50 | |||
| 46 |
|
||||
| 47 | } |
|
51 | } | |
| 48 |
|
52 | |||
| 49 | } |
|
53 | } | |
| @@ -1,85 +1,78 | |||||
| 1 | package org.implab.gradle.containers; |
|
1 | package org.implab.gradle.containers; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.io.FileNotFoundException; |
|
4 | import java.io.FileNotFoundException; | |
| 5 | import java.io.IOException; |
|
5 | import java.io.IOException; | |
| 6 | import java.util.Optional; |
|
6 | import java.util.Optional; | |
| 7 |
|
7 | |||
| 8 | import javax.inject.Inject; |
|
8 | import javax.inject.Inject; | |
| 9 |
|
9 | |||
| 10 | import org.gradle.api.Project; |
|
10 | import org.gradle.api.Project; | |
| 11 | import org.gradle.api.file.DirectoryProperty; |
|
11 | import org.gradle.api.file.DirectoryProperty; | |
| 12 | import org.gradle.api.file.ProjectLayout; |
|
12 | import org.gradle.api.file.ProjectLayout; | |
| 13 | import org.gradle.api.file.RegularFileProperty; |
|
|||
| 14 | import org.gradle.api.provider.Property; |
|
13 | import org.gradle.api.provider.Property; | |
| 15 | import org.gradle.api.provider.Provider; |
|
14 | import org.gradle.api.provider.Provider; | |
| 16 | import org.implab.gradle.containers.cli.ImageName; |
|
15 | import org.implab.gradle.containers.cli.ImageName; | |
| 17 | import org.implab.gradle.containers.cli.ImageRef; |
|
16 | import org.implab.gradle.containers.cli.ImageRef; | |
| 18 | import org.implab.gradle.containers.cli.Utils; |
|
17 | import org.implab.gradle.containers.cli.Utils; | |
| 19 |
|
18 | |||
| 20 | public abstract class ContainerExtension { |
|
19 | public abstract class ContainerExtension implements PropertiesMixin { | |
| 21 |
|
20 | |||
| 22 | public abstract Property<String> getCliCmd(); |
|
21 | public abstract Property<String> getCliCmd(); | |
| 23 |
|
22 | |||
| 24 | public abstract DirectoryProperty getContextDirectory(); |
|
23 | public abstract DirectoryProperty getContextDirectory(); | |
| 25 |
|
24 | |||
| 26 | public abstract RegularFileProperty getImageIdFile(); |
|
|||
| 27 |
|
||||
| 28 | /** |
|
25 | /** | |
| 29 | * Specifies the name of the registry where the image is located |
|
26 | * Specifies the name of the registry where the image is located | |
| 30 | * {@code registry.my-company.org} |
|
27 | * {@code registry.my-company.org} | |
| 31 | */ |
|
28 | */ | |
| 32 | public abstract Property<String> getImageAuthority(); |
|
29 | public abstract Property<String> getImageAuthority(); | |
| 33 |
|
30 | |||
| 34 | /** |
|
31 | /** | |
| 35 | * Specified the path of the image like {@code my-company} |
|
32 | * Specified the path of the image like {@code my-company} | |
| 36 | */ |
|
33 | */ | |
| 37 | public abstract Property<String> getImageGroup(); |
|
34 | public abstract Property<String> getImageGroup(); | |
| 38 |
|
35 | |||
| 39 |
public |
|
36 | public Provider<ImageName> getImageName() { | |
|
|
37 | return provider(this::createImageName); | |||
|
|
38 | } | |||
| 40 |
|
39 | |||
| 41 | /** |
|
40 | /** | |
| 42 | * Specifies local image part like {@code httpd} |
|
41 | * Specifies local image part like {@code httpd} | |
| 43 | */ |
|
42 | */ | |
| 44 | public abstract Property<String> getImageLocalName(); |
|
43 | public abstract Property<String> getImageLocalName(); | |
| 45 |
|
44 | |||
| 46 | /** |
|
45 | /** | |
| 47 | * This property is deprecated use imageLocalName |
|
46 | * This property is deprecated use imageLocalName | |
| 48 | */ |
|
47 | */ | |
| 49 | @Deprecated |
|
48 | @Deprecated | |
| 50 | public Property<String> getImageShortName() { |
|
49 | public Property<String> getImageShortName() { | |
| 51 | return getImageLocalName(); |
|
50 | return getImageLocalName(); | |
| 52 | } |
|
51 | } | |
| 53 |
|
52 | |||
| 54 | public abstract Property<String> getImageTag(); |
|
53 | public abstract Property<String> getImageTag(); | |
| 55 |
|
54 | |||
| 56 | @Inject |
|
55 | @Inject | |
| 57 | public ContainerExtension(ProjectLayout layout, Project project) { |
|
56 | public ContainerExtension(ProjectLayout layout, Project project) { | |
| 58 | getContextDirectory().convention(layout.getBuildDirectory().dir("context")); |
|
57 | getContextDirectory().convention(layout.getBuildDirectory().dir("context")); | |
| 59 |
|
58 | |||
| 60 | getImageIdFile().convention(layout.getBuildDirectory().file("iid.json")); |
|
|||
| 61 |
|
||||
| 62 | getCliCmd().set("docker"); |
|
|||
| 63 |
|
||||
| 64 | getImageLocalName().convention(project.getName()); |
|
59 | getImageLocalName().convention(project.getName()); | |
| 65 |
|
60 | |||
| 66 | getImageTag().set(project |
|
61 | getImageTag().set(provider(() -> Optional.ofNullable(project.getVersion()) | |
| 67 | .provider(() -> Optional.ofNullable(project.getVersion()).map(v -> v.toString()).orElse("latest"))); |
|
62 | .map(Object::toString) | |
| 68 |
|
63 | .orElse("latest"))); | ||
| 69 | Provider<String> imageRepository = getImageGroup().map(g -> g + "/" + getImageLocalName().get()) |
|
|||
| 70 | .orElse(getImageLocalName()); |
|
|||
| 71 |
|
||||
| 72 | getImageName().convention(project.provider( |
|
|||
| 73 | () -> new ImageName().authority(getImageAuthority().get()).name(imageRepository.get()) |
|
|||
| 74 | .tag(getImageTag().get()))); |
|
|||
| 75 | } |
|
64 | } | |
| 76 |
|
65 | |||
| 77 |
|
|
66 | ImageName createImageName() { | |
| 78 |
return new ImageName( |
|
67 | return new ImageName( | |
|
|
68 | getImageAuthority().getOrNull(), | |||
|
|
69 | getImageGroup().getOrNull(), | |||
|
|
70 | getImageLocalName().get(), | |||
|
|
71 | getImageTag().getOrNull()); | |||
| 79 | } |
|
72 | } | |
| 80 |
|
73 | |||
| 81 | public ImageRef readImageRef(File file) throws FileNotFoundException, IOException { |
|
74 | public ImageRef readImageRef(File file) throws FileNotFoundException, IOException { | |
| 82 | return Utils.readImageRef(file); |
|
75 | return Utils.readImageRef(file); | |
| 83 | } |
|
76 | } | |
| 84 |
|
77 | |||
| 85 | } No newline at end of file |
|
78 | } | |
| @@ -1,82 +1,80 | |||||
| 1 | package org.implab.gradle.containers; |
|
1 | package org.implab.gradle.containers; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.GradleException; |
|
3 | import org.gradle.api.GradleException; | |
| 4 | import org.gradle.api.Plugin; |
|
4 | import org.gradle.api.Plugin; | |
| 5 | import org.gradle.api.Project; |
|
5 | import org.gradle.api.Project; | |
| 6 | import org.gradle.api.artifacts.Dependency; |
|
6 | import org.gradle.api.artifacts.Dependency; | |
| 7 | import org.gradle.api.file.ProjectLayout; |
|
7 | import org.gradle.api.file.ProjectLayout; | |
| 8 | import org.gradle.api.tasks.Copy; |
|
8 | import org.gradle.api.tasks.Copy; | |
| 9 | import org.gradle.api.tasks.Delete; |
|
9 | import org.gradle.api.tasks.Delete; | |
| 10 | import org.gradle.api.tasks.TaskProvider; |
|
10 | import org.gradle.api.tasks.TaskProvider; | |
| 11 | import org.implab.gradle.containers.cli.ImageName; |
|
11 | import org.implab.gradle.containers.cli.ImageName; | |
| 12 | import org.implab.gradle.containers.tasks.BuildImage; |
|
12 | import org.implab.gradle.containers.tasks.BuildImage; | |
| 13 | import org.implab.gradle.containers.tasks.PushImage; |
|
13 | import org.implab.gradle.containers.tasks.PushImage; | |
| 14 | import org.implab.gradle.containers.tasks.SaveImage; |
|
14 | import org.implab.gradle.containers.tasks.SaveImage; | |
| 15 |
|
15 | |||
| 16 | public class ContainerPlugin implements Plugin<Project> { |
|
16 | public class ContainerPlugin implements Plugin<Project> { | |
| 17 |
|
17 | |||
| 18 | public static final String BUILD_GROUP = "build"; |
|
18 | public static final String BUILD_GROUP = "build"; | |
| 19 |
|
19 | |||
| 20 | public static final String ARCHIVE_CONFIGURATION = "archive"; |
|
20 | public static final String ARCHIVE_CONFIGURATION = "archive"; | |
| 21 |
|
21 | |||
| 22 | ContainerExtension containerExtension; |
|
|||
| 23 |
|
||||
| 24 | public void apply(Project project) { |
|
22 | public void apply(Project project) { | |
| 25 | ProjectLayout layout = project.getLayout(); |
|
23 | ProjectLayout layout = project.getLayout(); | |
| 26 |
|
24 | |||
| 27 | project.getPluginManager().apply(ContainerBasePlugin.class); |
|
25 | project.getPluginManager().apply(ContainerBasePlugin.class); | |
| 28 |
|
26 | |||
| 29 | var basePlugin = project.getPlugins().findPlugin(ContainerBasePlugin.class); |
|
27 | var basePlugin = project.getPlugins().findPlugin(ContainerBasePlugin.class); | |
| 30 | if (basePlugin == null) |
|
28 | if (basePlugin == null) | |
| 31 | throw new GradleException("The container-base plugin fails to be applied"); |
|
29 | throw new GradleException("The container-base plugin fails to be applied"); | |
| 32 |
|
30 | |||
| 33 | var containerExtension = basePlugin.getContainerExtension(); |
|
31 | var containerExtension = basePlugin.getContainerExtension(); | |
| 34 |
|
32 | |||
| 35 | project.getConfigurations().create(Dependency.DEFAULT_CONFIGURATION, c -> { |
|
33 | project.getConfigurations().create(Dependency.DEFAULT_CONFIGURATION, c -> { | |
| 36 | c.setCanBeConsumed(true); |
|
34 | c.setCanBeConsumed(true); | |
| 37 | c.setCanBeResolved(false); |
|
35 | c.setCanBeResolved(false); | |
| 38 | }); |
|
36 | }); | |
| 39 |
|
37 | |||
| 40 | project.getConfigurations().create(ARCHIVE_CONFIGURATION, c -> { |
|
38 | project.getConfigurations().create(ARCHIVE_CONFIGURATION, c -> { | |
| 41 | c.setCanBeConsumed(true); |
|
39 | c.setCanBeConsumed(true); | |
| 42 | c.setCanBeResolved(false); |
|
40 | c.setCanBeResolved(false); | |
| 43 | }); |
|
41 | }); | |
| 44 |
|
42 | |||
| 45 | TaskProvider<Copy> processResourcesTask = project.getTasks().register("processResources", Copy.class, t -> { |
|
43 | TaskProvider<Copy> processResourcesTask = project.getTasks().register("processResources", Copy.class, t -> { | |
| 46 | t.setGroup(BUILD_GROUP); |
|
44 | t.setGroup(BUILD_GROUP); | |
| 47 | t.from(layout.getProjectDirectory().dir("src/main")); |
|
45 | t.from(layout.getProjectDirectory().dir("src/main")); | |
| 48 | t.into(containerExtension.getContextDirectory()); |
|
46 | t.into(containerExtension.getContextDirectory()); | |
| 49 | }); |
|
47 | }); | |
| 50 |
|
48 | |||
| 51 | TaskProvider<BuildImage> buildImageTask = project.getTasks().register("buildImage", BuildImage.class, t -> { |
|
49 | TaskProvider<BuildImage> buildImageTask = project.getTasks().register("buildImage", BuildImage.class, t -> { | |
| 52 | t.setGroup(BUILD_GROUP); |
|
50 | t.setGroup(BUILD_GROUP); | |
| 53 | t.dependsOn(processResourcesTask); |
|
51 | t.dependsOn(processResourcesTask); | |
| 54 |
|
52 | |||
| 55 | t.getContextDirectory().set(containerExtension.getContextDirectory()); |
|
53 | t.getContextDirectory().set(containerExtension.getContextDirectory()); | |
| 56 |
t.getImageIdFile().set( |
|
54 | t.getImageIdFile().set(project.getLayout().getBuildDirectory().file("iid.json")); | |
| 57 |
|
55 | |||
| 58 | t.getImageName().set(containerExtension.getImageName()); |
|
56 | t.getImageName().set(containerExtension.getImageName().map(ImageName::toString)); | |
| 59 | }); |
|
57 | }); | |
| 60 |
|
58 | |||
| 61 | project.getTasks().register("clean", Delete.class, t -> { |
|
59 | project.getTasks().register("clean", Delete.class, t -> { | |
| 62 | t.delete(layout.getBuildDirectory()); |
|
60 | t.delete(layout.getBuildDirectory()); | |
| 63 | }); |
|
61 | }); | |
| 64 |
|
62 | |||
| 65 | project.getTasks().register("build", t -> { |
|
63 | project.getTasks().register("build", t -> { | |
| 66 | t.setGroup(BUILD_GROUP); |
|
64 | t.setGroup(BUILD_GROUP); | |
| 67 | t.dependsOn(buildImageTask); |
|
65 | t.dependsOn(buildImageTask); | |
| 68 | }); |
|
66 | }); | |
| 69 |
|
67 | |||
| 70 | project.getTasks().register("pushImage", PushImage.class, t -> { |
|
68 | project.getTasks().register("pushImage", PushImage.class, t -> { | |
| 71 | t.dependsOn(buildImageTask); |
|
69 | t.dependsOn(buildImageTask); | |
| 72 | t.getImageName().set(buildImageTask.flatMap(BuildImage::getImageName)); |
|
70 | t.getImageName().set(buildImageTask.flatMap(BuildImage::getImageName)); | |
| 73 | }); |
|
71 | }); | |
| 74 |
|
72 | |||
| 75 | project.getTasks().register("saveImage", SaveImage.class, t -> { |
|
73 | project.getTasks().register("saveImage", SaveImage.class, t -> { | |
| 76 | t.dependsOn(buildImageTask); |
|
74 | t.dependsOn(buildImageTask); | |
| 77 |
t.getExportImages().add(buildImageTask.flatMap(BuildImage::getImageName) |
|
75 | t.getExportImages().add(buildImageTask.flatMap(BuildImage::getImageName)); | |
| 78 | }); |
|
76 | }); | |
| 79 |
|
77 | |||
| 80 | project.getArtifacts().add(Dependency.DEFAULT_CONFIGURATION, buildImageTask); |
|
78 | project.getArtifacts().add(Dependency.DEFAULT_CONFIGURATION, buildImageTask); | |
| 81 | } |
|
79 | } | |
| 82 | } |
|
80 | } | |
| @@ -1,142 +1,141 | |||||
| 1 | package org.implab.gradle.containers.cli; |
|
1 | package org.implab.gradle.containers.cli; | |
| 2 |
|
2 | |||
| 3 | import java.io.File; |
|
3 | import java.io.File; | |
| 4 | import java.io.IOException; |
|
4 | import java.io.IOException; | |
| 5 | import java.nio.file.Files; |
|
|||
| 6 | import java.util.ArrayList; |
|
5 | import java.util.ArrayList; | |
| 7 | import java.util.Arrays; |
|
6 | import java.util.Arrays; | |
| 8 | import java.util.List; |
|
7 | import java.util.List; | |
| 9 | import java.util.Optional; |
|
8 | import java.util.Optional; | |
| 10 |
|
9 | |||
| 11 | import org.gradle.api.logging.Logger; |
|
10 | import org.gradle.api.logging.Logger; | |
| 12 |
|
11 | |||
| 13 | public abstract class DockerTraits { |
|
12 | public abstract class DockerTraits { | |
| 14 |
|
13 | |||
| 15 | public final String BUILD_COMMAND = "build"; |
|
14 | public final String BUILD_COMMAND = "build"; | |
| 16 | public final String PUSH_COMMAND = "push"; |
|
15 | public final String PUSH_COMMAND = "push"; | |
| 17 | public final String RUN_COMMAND = "run"; |
|
16 | public final String RUN_COMMAND = "run"; | |
| 18 | public final String SAVE_COMMAND = "save"; |
|
17 | public final String SAVE_COMMAND = "save"; | |
| 19 | public final String INSPECT_COMMAND = "inspect"; |
|
18 | public final String INSPECT_COMMAND = "inspect"; | |
| 20 | public final String IMAGE_COMMAND = "image"; |
|
19 | public final String IMAGE_COMMAND = "image"; | |
| 21 | public final String TAG_COMMAND = "tag"; |
|
20 | public final String TAG_COMMAND = "tag"; | |
| 22 |
|
21 | |||
| 23 | public abstract Logger getLogger(); |
|
22 | public abstract Logger getLogger(); | |
| 24 |
|
23 | |||
| 25 | public abstract Optional<File> getWorkingDir(); |
|
24 | public abstract Optional<File> getWorkingDir(); | |
| 26 |
|
25 | |||
| 27 | public abstract String getCliCmd(); |
|
26 | public abstract String getCliCmd(); | |
| 28 |
|
27 | |||
| 29 | Process startProcess(ProcessBuilder builder) throws IOException { |
|
28 | Process startProcess(ProcessBuilder builder) throws IOException { | |
| 30 | getLogger().info("Starting: {}", builder.command()); |
|
29 | getLogger().info("Starting: {}", builder.command()); | |
| 31 | return builder.start(); |
|
30 | return builder.start(); | |
| 32 | } |
|
31 | } | |
| 33 |
|
32 | |||
| 34 | protected boolean checkRetCode(Process proc, int code) throws InterruptedException { |
|
33 | protected boolean checkRetCode(Process proc, int code) throws InterruptedException { | |
| 35 | if (getLogger().isInfoEnabled()) { |
|
34 | if (getLogger().isInfoEnabled()) { | |
| 36 | Utils.redirectIO(proc.getInputStream(), getLogger()::info); |
|
35 | Utils.redirectIO(proc.getInputStream(), getLogger()::info); | |
| 37 | Utils.redirectIO(proc.getErrorStream(), getLogger()::info); |
|
36 | Utils.redirectIO(proc.getErrorStream(), getLogger()::info); | |
| 38 | } |
|
37 | } | |
| 39 |
|
38 | |||
| 40 | return proc.waitFor() == code; |
|
39 | return proc.waitFor() == code; | |
| 41 | } |
|
40 | } | |
| 42 |
|
41 | |||
| 43 | protected void complete(Process proc) throws InterruptedException, IOException { |
|
42 | protected void complete(Process proc) throws InterruptedException, IOException { | |
| 44 | if (getLogger().isInfoEnabled()) |
|
43 | if (getLogger().isInfoEnabled()) | |
| 45 | Utils.redirectIO(proc.getInputStream(), getLogger()::info); |
|
44 | Utils.redirectIO(proc.getInputStream(), getLogger()::info); | |
| 46 |
|
45 | |||
| 47 | if (getLogger().isErrorEnabled()) |
|
46 | if (getLogger().isErrorEnabled()) | |
| 48 | Utils.redirectIO(proc.getErrorStream(), getLogger()::error); |
|
47 | Utils.redirectIO(proc.getErrorStream(), getLogger()::error); | |
| 49 |
|
48 | |||
| 50 | var code = proc.waitFor(); |
|
49 | var code = proc.waitFor(); | |
| 51 | if (code != 0) { |
|
50 | if (code != 0) { | |
| 52 | getLogger().error("The process exited with code {}", code); |
|
51 | getLogger().error("The process exited with code {}", code); | |
| 53 | throw new IOException("The process exited with error code " + code); |
|
52 | throw new IOException("The process exited with error code " + code); | |
| 54 | } |
|
53 | } | |
| 55 | } |
|
54 | } | |
| 56 |
|
55 | |||
| 57 | protected ProcessBuilder builder(String... args) { |
|
56 | protected ProcessBuilder builder(String... args) { | |
| 58 | var argsList = new ArrayList<String>(args.length + 1); |
|
57 | var argsList = new ArrayList<String>(args.length + 1); | |
| 59 | Arrays.stream(args).forEach(argsList::add); |
|
58 | Arrays.stream(args).forEach(argsList::add); | |
| 60 |
|
59 | |||
| 61 | return builder(argsList); |
|
60 | return builder(argsList); | |
| 62 | } |
|
61 | } | |
| 63 |
|
62 | |||
| 64 | protected ProcessBuilder builder(List<String> args) { |
|
63 | protected ProcessBuilder builder(List<String> args) { | |
| 65 | var command = new ArrayList<String>(args.size() + 1); |
|
64 | var command = new ArrayList<String>(args.size() + 1); | |
| 66 |
|
65 | |||
| 67 | command.add(getCliCmd()); |
|
66 | command.add(getCliCmd()); | |
| 68 | args.forEach(command::add); |
|
67 | args.forEach(command::add); | |
| 69 |
|
68 | |||
| 70 | var builder = new ProcessBuilder(command); |
|
69 | var builder = new ProcessBuilder(command); | |
| 71 |
|
70 | |||
| 72 | getWorkingDir().ifPresent(builder::directory); |
|
71 | getWorkingDir().ifPresent(builder::directory); | |
| 73 | return builder; |
|
72 | return builder; | |
| 74 | } |
|
73 | } | |
| 75 |
|
74 | |||
| 76 | public void buildImage(String imageName, File contextDirectory, List<String> options) |
|
75 | public void buildImage(String imageName, File contextDirectory, List<String> options) | |
| 77 | throws IOException, InterruptedException { |
|
76 | throws IOException, InterruptedException { | |
| 78 | var args = new ArrayList<String>(); |
|
77 | var args = new ArrayList<String>(); | |
| 79 | args.add(BUILD_COMMAND); |
|
78 | args.add(BUILD_COMMAND); | |
| 80 | args.addAll(options); |
|
79 | args.addAll(options); | |
| 81 | args.add("-t"); |
|
80 | args.add("-t"); | |
| 82 | args.add(imageName); |
|
81 | args.add(imageName); | |
| 83 | args.add(contextDirectory.getAbsolutePath()); |
|
82 | args.add(contextDirectory.getAbsolutePath()); | |
| 84 | complete(startProcess(builder(args))); |
|
83 | complete(startProcess(builder(args))); | |
| 85 | } |
|
84 | } | |
| 86 |
|
85 | |||
| 87 | public void pushImage(String image, List<String> options) throws InterruptedException, IOException { |
|
86 | public void pushImage(String image, List<String> options) throws InterruptedException, IOException { | |
| 88 | var args = new ArrayList<String>(); |
|
87 | var args = new ArrayList<String>(); | |
| 89 | args.add(PUSH_COMMAND); |
|
88 | args.add(PUSH_COMMAND); | |
| 90 | args.addAll(options); |
|
89 | args.addAll(options); | |
| 91 | args.add(image); |
|
90 | args.add(image); | |
| 92 | complete(startProcess(builder(args))); |
|
91 | complete(startProcess(builder(args))); | |
| 93 | } |
|
92 | } | |
| 94 |
|
93 | |||
| 95 | public void runImage(String image, List<String> options, List<String> command) |
|
94 | public void runImage(String image, List<String> options, List<String> command) | |
| 96 | throws InterruptedException, IOException { |
|
95 | throws InterruptedException, IOException { | |
| 97 | var args = new ArrayList<String>(); |
|
96 | var args = new ArrayList<String>(); | |
| 98 | args.add(RUN_COMMAND); |
|
97 | args.add(RUN_COMMAND); | |
| 99 | args.addAll(options); |
|
98 | args.addAll(options); | |
| 100 | args.add(image); |
|
99 | args.add(image); | |
| 101 | args.addAll(command); |
|
100 | args.addAll(command); | |
| 102 | complete(startProcess(builder(args))); |
|
101 | complete(startProcess(builder(args))); | |
| 103 | } |
|
102 | } | |
| 104 |
|
103 | |||
| 105 | public void saveImage(List<String> images, File output) throws InterruptedException, IOException { |
|
104 | public void saveImage(List<String> images, File output) throws InterruptedException, IOException { | |
| 106 | if (output.exists()) |
|
105 | if (output.exists()) | |
| 107 | output.delete(); |
|
106 | output.delete(); | |
| 108 |
|
107 | |||
| 109 | var args = new ArrayList<String>(); |
|
108 | var args = new ArrayList<String>(); | |
| 110 | args.add(SAVE_COMMAND); |
|
109 | args.add(SAVE_COMMAND); | |
| 111 | args.add("-o"); |
|
110 | args.add("-o"); | |
| 112 | args.add(output.getAbsolutePath()); |
|
111 | args.add(output.getAbsolutePath()); | |
| 113 | images.forEach(args::add); |
|
112 | images.forEach(args::add); | |
| 114 |
|
113 | |||
| 115 | complete(startProcess(builder(args))); |
|
114 | complete(startProcess(builder(args))); | |
| 116 | } |
|
115 | } | |
| 117 |
|
116 | |||
| 118 | public void tagImage(String source, String target) throws InterruptedException, IOException { |
|
117 | public void tagImage(String source, String target) throws InterruptedException, IOException { | |
| 119 | complete(startProcess(builder(TAG_COMMAND, source, target))); |
|
118 | complete(startProcess(builder(TAG_COMMAND, source, target))); | |
| 120 | } |
|
119 | } | |
| 121 |
|
120 | |||
| 122 | public boolean imageExists(String imageId) throws InterruptedException, IOException { |
|
121 | public boolean imageExists(String imageId) throws InterruptedException, IOException { | |
| 123 | getLogger().info("Check image {} exists", imageId); |
|
122 | getLogger().info("Check image {} exists", imageId); | |
| 124 |
|
123 | |||
| 125 | return checkRetCode( |
|
124 | return checkRetCode( | |
| 126 | startProcess(builder(IMAGE_COMMAND, INSPECT_COMMAND, "--format", "image-exists", imageId)), |
|
125 | startProcess(builder(IMAGE_COMMAND, INSPECT_COMMAND, "--format", "image-exists", imageId)), | |
| 127 | 0); |
|
126 | 0); | |
| 128 | } |
|
127 | } | |
| 129 |
|
128 | |||
| 130 | public boolean imageExists(File imageIdFile) { |
|
129 | public boolean imageExists(File imageIdFile) { | |
| 131 | if (imageIdFile.exists()) { |
|
130 | if (imageIdFile.exists()) { | |
| 132 | try { |
|
131 | try { | |
| 133 | var imageId = Utils.readImageRef(imageIdFile); |
|
132 | var imageId = Utils.readImageRef(imageIdFile); | |
| 134 | return imageExists(imageId.getId()); |
|
133 | return imageExists(imageId.getId()); | |
| 135 | } catch (IOException | InterruptedException e) { |
|
134 | } catch (IOException | InterruptedException e) { | |
| 136 | getLogger().error("Failed to read imageId {}: {}", imageIdFile, e); |
|
135 | getLogger().error("Failed to read imageId {}: {}", imageIdFile, e); | |
| 137 | return false; |
|
136 | return false; | |
| 138 | } |
|
137 | } | |
| 139 | } |
|
138 | } | |
| 140 | return false; |
|
139 | return false; | |
| 141 | } |
|
140 | } | |
| 142 | } |
|
141 | } | |
| @@ -1,71 +1,83 | |||||
| 1 | package org.implab.gradle.containers.cli; |
|
1 | package org.implab.gradle.containers.cli; | |
| 2 |
|
2 | |||
| 3 | import java.io.Serializable; |
|
3 | import java.io.Serializable; | |
|
|
4 | import java.util.ArrayList; | |||
| 4 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
| 5 |
|
6 | |||
| 6 | import com.fasterxml.jackson.annotation.JsonCreator; |
|
7 | import com.fasterxml.jackson.annotation.JsonCreator; | |
| 7 | import com.fasterxml.jackson.annotation.JsonProperty; |
|
8 | import com.fasterxml.jackson.annotation.JsonProperty; | |
| 8 |
|
9 | |||
| 9 | public class ImageName implements Serializable { |
|
10 | public class ImageName implements Serializable { | |
| 10 |
|
11 | |||
| 11 | private static final long serialVersionUID = -1990105923537254552L; |
|
12 | private static final long serialVersionUID = -1990105923537254552L; | |
| 12 |
|
13 | |||
| 13 | final String authority; |
|
14 | final String authority; | |
| 14 |
|
15 | |||
| 15 |
final String |
|
16 | final String groupName; | |
|
|
17 | ||||
|
|
18 | final String localName; | |||
| 16 |
|
19 | |||
| 17 | final String tag; |
|
20 | final String tag; | |
| 18 |
|
21 | |||
| 19 | public ImageName() { |
|
22 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) | |
| 20 | name = null; |
|
23 | public ImageName( | |
| 21 | authority = null; |
|
24 | @JsonProperty("authority") String authority, | |
| 22 | tag = null; |
|
25 | @JsonProperty("groupName") String groupName, | |
| 23 | } |
|
26 | @JsonProperty("localName") String localName, | |
|
|
27 | @JsonProperty("tag") String tag) { | |||
|
|
28 | if (localName == null || localName.isBlank()) | |||
|
|
29 | throw new IllegalArgumentException("localName can't be null or blank string"); | |||
| 24 |
|
30 | |||
| 25 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) |
|
|||
| 26 | private ImageName(@JsonProperty("authority") String authority, @JsonProperty("name") String name, |
|
|||
| 27 | @JsonProperty("tag") String tag) { |
|
|||
| 28 | this.authority = authority; |
|
31 | this.authority = authority; | |
| 29 |
this. |
|
32 | this.groupName = groupName; | |
|
|
33 | this.localName = localName; | |||
| 30 | this.tag = tag; |
|
34 | this.tag = tag; | |
| 31 | } |
|
35 | } | |
| 32 |
|
36 | |||
| 33 | public String getAuthority() { |
|
37 | public Optional<String> getAuthority() { | |
| 34 | return authority; |
|
38 | return Optional.ofNullable(authority); | |
| 35 | } |
|
39 | } | |
| 36 |
|
40 | |||
| 37 |
public ImageName |
|
41 | public ImageName withAuthority(String authority) { | |
| 38 |
return new ImageName(authority, |
|
42 | return new ImageName(authority, groupName, localName, tag); | |
| 39 | } |
|
43 | } | |
| 40 |
|
44 | |||
| 41 | public String getName() { |
|
45 | public Optional<String> getGroupName() { | |
| 42 | return name; |
|
46 | return Optional.ofNullable(groupName); | |
|
|
47 | } | |||
|
|
48 | ||||
|
|
49 | public ImageName withGroupName(String groupName) { | |||
|
|
50 | return new ImageName(authority, groupName, localName, tag); | |||
| 43 | } |
|
51 | } | |
| 44 |
|
52 | |||
| 45 |
public |
|
53 | public String getLocalName() { | |
| 46 | return new ImageName(authority, name, tag); |
|
54 | return localName; | |
| 47 | } |
|
55 | } | |
| 48 |
|
56 | |||
| 49 | public String getTag() { |
|
57 | public ImageName withLocalName(String localName) { | |
| 50 | return tag; |
|
58 | if (localName == null || localName.isBlank()) | |
|
|
59 | throw new IllegalArgumentException("localName can't be null or blank string"); | |||
|
|
60 | return new ImageName(authority, groupName, localName, tag); | |||
| 51 | } |
|
61 | } | |
| 52 |
|
62 | |||
| 53 |
public |
|
63 | public Optional<String> getTag() { | |
| 54 | return new ImageName(authority, name, tag); |
|
64 | return Optional.ofNullable(tag); | |
| 55 | } |
|
65 | } | |
| 56 |
|
66 | |||
| 57 |
public ImageName |
|
67 | public ImageName withTag(String tag) { | |
| 58 |
return new ImageName(authority, |
|
68 | return new ImageName(authority, groupName, localName, tag); | |
| 59 | } |
|
69 | } | |
| 60 |
|
70 | |||
| 61 | @Override |
|
71 | @Override | |
| 62 | public String toString() { |
|
72 | public String toString() { | |
| 63 | StringBuilder sb = new StringBuilder(); |
|
73 | var path = new ArrayList<String>(); | |
| 64 |
|
74 | |||
| 65 | Optional.ofNullable(authority).ifPresent(s -> sb.append(s).append("/")); |
|
75 | getAuthority().ifPresent(path::add); | |
| 66 | Optional.ofNullable(name).ifPresent(s -> sb.append(s)); |
|
76 | getGroupName().ifPresent(path::add); | |
| 67 | Optional.ofNullable(tag).ifPresent(s -> sb.append(":").append(s)); |
|
77 | path.add(getLocalName()); | |
| 68 |
|
78 | |||
| 69 | return sb.toString(); |
|
79 | var repo = String.join("/", path); | |
|
|
80 | ||||
|
|
81 | return getTag().map(tag -> String.join(":", repo, tag)).orElse(repo); | |||
| 70 | } |
|
82 | } | |
| 71 | } |
|
83 | } | |
| @@ -1,116 +1,116 | |||||
| 1 | package org.implab.gradle.containers.tasks; |
|
1 | package org.implab.gradle.containers.tasks; | |
| 2 |
|
2 | |||
| 3 | import java.util.ArrayList; |
|
3 | import java.util.ArrayList; | |
| 4 | import java.util.HashMap; |
|
4 | import java.util.HashMap; | |
| 5 | import java.util.List; |
|
5 | import java.util.List; | |
| 6 | import java.util.Map; |
|
6 | import java.util.Map; | |
| 7 |
|
7 | |||
| 8 | import org.gradle.api.Action; |
|
8 | import org.gradle.api.Action; | |
| 9 | import org.gradle.api.file.Directory; |
|
9 | import org.gradle.api.file.Directory; | |
| 10 | import org.gradle.api.file.DirectoryProperty; |
|
10 | import org.gradle.api.file.DirectoryProperty; | |
| 11 | import org.gradle.api.file.RegularFile; |
|
11 | import org.gradle.api.file.RegularFile; | |
| 12 | import org.gradle.api.file.RegularFileProperty; |
|
12 | import org.gradle.api.file.RegularFileProperty; | |
| 13 | import org.gradle.api.provider.MapProperty; |
|
13 | import org.gradle.api.provider.MapProperty; | |
| 14 | import org.gradle.api.provider.Property; |
|
14 | import org.gradle.api.provider.Property; | |
| 15 | import org.gradle.api.provider.Provider; |
|
15 | import org.gradle.api.provider.Provider; | |
| 16 | import org.gradle.api.tasks.Input; |
|
16 | import org.gradle.api.tasks.Input; | |
| 17 | import org.gradle.api.tasks.InputDirectory; |
|
17 | import org.gradle.api.tasks.InputDirectory; | |
| 18 | import org.gradle.api.tasks.Internal; |
|
18 | import org.gradle.api.tasks.Internal; | |
| 19 | import org.gradle.api.tasks.Optional; |
|
19 | import org.gradle.api.tasks.Optional; | |
| 20 | import org.gradle.api.tasks.OutputFile; |
|
20 | import org.gradle.api.tasks.OutputFile; | |
| 21 | import org.gradle.api.tasks.SkipWhenEmpty; |
|
21 | import org.gradle.api.tasks.SkipWhenEmpty; | |
| 22 | import org.gradle.api.tasks.TaskAction; |
|
22 | import org.gradle.api.tasks.TaskAction; | |
| 23 | import java.io.File; |
|
23 | import java.io.File; | |
| 24 |
|
24 | |||
| 25 | import org.implab.gradle.containers.cli.ImageName; |
|
|||
| 26 | import org.implab.gradle.containers.cli.ImageRef; |
|
25 | import org.implab.gradle.containers.cli.ImageRef; | |
| 27 | import org.implab.gradle.containers.cli.Utils; |
|
26 | import org.implab.gradle.containers.cli.Utils; | |
| 28 | import org.implab.gradle.containers.dsl.MapPropertyEntry; |
|
27 | import org.implab.gradle.containers.dsl.MapPropertyEntry; | |
| 29 | import org.implab.gradle.containers.dsl.OptionsMixin; |
|
28 | import org.implab.gradle.containers.dsl.OptionsMixin; | |
| 30 |
|
29 | |||
| 31 | import groovy.lang.Closure; |
|
30 | import groovy.lang.Closure; | |
| 32 |
|
31 | |||
| 33 | public abstract class BuildImage extends DockerCliTask implements OptionsMixin { |
|
32 | public abstract class BuildImage extends DockerCliTask implements OptionsMixin { | |
|
|
33 | ||||
| 34 | @InputDirectory |
|
34 | @InputDirectory | |
| 35 | @SkipWhenEmpty |
|
35 | @SkipWhenEmpty | |
| 36 | public abstract DirectoryProperty getContextDirectory(); |
|
36 | public abstract DirectoryProperty getContextDirectory(); | |
| 37 |
|
37 | |||
| 38 | @Input |
|
38 | @Input | |
| 39 | public abstract MapProperty<String, String> getBuildArgs(); |
|
39 | public abstract MapProperty<String, String> getBuildArgs(); | |
| 40 |
|
40 | |||
| 41 | @Input |
|
41 | @Input | |
| 42 | @Optional |
|
42 | @Optional | |
| 43 | public abstract Property<String> getBuildTarget(); |
|
43 | public abstract Property<String> getBuildTarget(); | |
| 44 |
|
44 | |||
| 45 | @Input |
|
45 | @Input | |
| 46 |
public abstract Property< |
|
46 | public abstract Property<String> getImageName(); | |
| 47 |
|
47 | |||
| 48 | @Internal |
|
48 | @Internal | |
| 49 | public abstract RegularFileProperty getImageIdFile(); |
|
49 | public abstract RegularFileProperty getImageIdFile(); | |
| 50 |
|
50 | |||
| 51 | @OutputFile |
|
51 | @OutputFile | |
| 52 | public Provider<File> getImageIdFileOutput() { |
|
52 | public Provider<File> getImageIdFileOutput() { | |
| 53 | return getImageIdFile().map(RegularFile::getAsFile); |
|
53 | return getImageIdFile().map(RegularFile::getAsFile); | |
| 54 | } |
|
54 | } | |
| 55 |
|
55 | |||
| 56 | public BuildImage() { |
|
56 | public BuildImage() { | |
| 57 | getOutputs().upToDateWhen(task -> getImageIdFile() |
|
57 | getOutputs().upToDateWhen(task -> getImageIdFile() | |
| 58 | .map(RegularFile::getAsFile) |
|
58 | .map(RegularFile::getAsFile) | |
| 59 | .map(docker()::imageExists) |
|
59 | .map(docker()::imageExists) | |
| 60 | .getOrElse(false)); |
|
60 | .getOrElse(false)); | |
| 61 | } |
|
61 | } | |
| 62 |
|
62 | |||
| 63 | public void buildArgs(Action<Map<String, String>> spec) { |
|
63 | public void buildArgs(Action<Map<String, String>> spec) { | |
| 64 | getBuildArgs().putAll(provider(() -> { |
|
64 | getBuildArgs().putAll(provider(() -> { | |
| 65 | Map<String, String> args = new HashMap<>(); |
|
65 | Map<String, String> args = new HashMap<>(); | |
| 66 | spec.execute(args); |
|
66 | spec.execute(args); | |
| 67 | getLogger().info("add buildArgs {}", args); |
|
67 | getLogger().info("add buildArgs {}", args); | |
| 68 | return args; |
|
68 | return args; | |
| 69 | })); |
|
69 | })); | |
| 70 | } |
|
70 | } | |
| 71 |
|
71 | |||
| 72 | public void buildArgs(Closure<Map<String, String>> spec) { |
|
72 | public void buildArgs(Closure<Map<String, String>> spec) { | |
| 73 | buildArgs(Utils.wrapClosure(spec)); |
|
73 | buildArgs(Utils.wrapClosure(spec)); | |
| 74 | } |
|
74 | } | |
| 75 |
|
75 | |||
| 76 | public MapPropertyEntry<String, String> buildArg(String key) { |
|
76 | public MapPropertyEntry<String, String> buildArg(String key) { | |
| 77 | return new MapPropertyEntry<String, String>(getBuildArgs(), key, getProviders()); |
|
77 | return new MapPropertyEntry<String, String>(getBuildArgs(), key, getProviders()); | |
| 78 | } |
|
78 | } | |
| 79 |
|
79 | |||
| 80 | @TaskAction |
|
80 | @TaskAction | |
| 81 | public void run() throws Exception { |
|
81 | public void run() throws Exception { | |
| 82 | List<String> args = new ArrayList<>(); |
|
82 | List<String> args = new ArrayList<>(); | |
| 83 |
|
83 | |||
| 84 | // create a temp file to store image id |
|
84 | // create a temp file to store image id | |
| 85 | var iidFile = new File(this.getTemporaryDir(), "iid"); |
|
85 | var iidFile = new File(this.getTemporaryDir(), "iid"); | |
| 86 |
|
86 | |||
| 87 | // specify where to write image id |
|
87 | // specify where to write image id | |
| 88 | args.addAll(List.of("--iidfile", iidFile.getAbsolutePath())); |
|
88 | args.addAll(List.of("--iidfile", iidFile.getAbsolutePath())); | |
| 89 |
|
89 | |||
| 90 | getBuildArgs().get().forEach((k, v) -> { |
|
90 | getBuildArgs().get().forEach((k, v) -> { | |
| 91 | args.add("--build-arg"); |
|
91 | args.add("--build-arg"); | |
| 92 | args.add(String.format("%s=%s", k, v)); |
|
92 | args.add(String.format("%s=%s", k, v)); | |
| 93 | }); |
|
93 | }); | |
| 94 |
|
94 | |||
| 95 | // add --target if specified for multi-stage build |
|
95 | // add --target if specified for multi-stage build | |
| 96 | if (getBuildTarget().isPresent()) { |
|
96 | if (getBuildTarget().isPresent()) { | |
| 97 | args.add("--target"); |
|
97 | args.add("--target"); | |
| 98 | args.add(getBuildTarget().get()); |
|
98 | args.add(getBuildTarget().get()); | |
| 99 | } |
|
99 | } | |
| 100 |
|
100 | |||
| 101 | // add extra parameters |
|
101 | // add extra parameters | |
| 102 | getOptions().get().forEach(args::add); |
|
102 | getOptions().get().forEach(args::add); | |
| 103 |
|
103 | |||
| 104 |
var imageTag = getImageName(). |
|
104 | var imageTag = getImageName().get(); | |
| 105 |
|
105 | |||
| 106 | // build image |
|
106 | // build image | |
| 107 | docker().buildImage( |
|
107 | docker().buildImage( | |
| 108 | imageTag, |
|
108 | imageTag, | |
| 109 | getContextDirectory().map(Directory::getAsFile).get(), |
|
109 | getContextDirectory().map(Directory::getAsFile).get(), | |
| 110 | args); |
|
110 | args); | |
| 111 |
|
111 | |||
| 112 | // read the built image id and store image ref metadata |
|
112 | // read the built image id and store image ref metadata | |
| 113 | var imageRef = new ImageRef(imageTag, Utils.readAll(iidFile)); |
|
113 | var imageRef = new ImageRef(imageTag, Utils.readAll(iidFile)); | |
| 114 |
Utils.writeJson(getImageIdFile |
|
114 | Utils.writeJson(getImageIdFile().map(RegularFile::getAsFile).get(), imageRef); | |
| 115 | } |
|
115 | } | |
| 116 | } |
|
116 | } | |
| @@ -1,33 +1,32 | |||||
| 1 | package org.implab.gradle.containers.tasks; |
|
1 | package org.implab.gradle.containers.tasks; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 |
|
4 | |||
| 5 | import org.gradle.api.provider.ListProperty; |
|
5 | import org.gradle.api.provider.ListProperty; | |
| 6 | import org.gradle.api.provider.Property; |
|
6 | import org.gradle.api.provider.Property; | |
| 7 | import org.gradle.api.tasks.Input; |
|
7 | import org.gradle.api.tasks.Input; | |
| 8 | import org.gradle.api.tasks.Optional; |
|
8 | import org.gradle.api.tasks.Optional; | |
| 9 | import org.gradle.api.tasks.TaskAction; |
|
9 | import org.gradle.api.tasks.TaskAction; | |
| 10 | import org.implab.gradle.containers.cli.ImageName; |
|
|||
| 11 | import org.implab.gradle.containers.dsl.OptionsMixin; |
|
10 | import org.implab.gradle.containers.dsl.OptionsMixin; | |
| 12 |
|
11 | |||
| 13 | public abstract class PushImage extends DockerCliTask implements OptionsMixin { |
|
12 | public abstract class PushImage extends DockerCliTask implements OptionsMixin { | |
| 14 |
|
13 | |||
| 15 | @Input |
|
14 | @Input | |
| 16 |
public abstract Property< |
|
15 | public abstract Property<String> getImageName(); | |
| 17 |
|
16 | |||
| 18 | @Input |
|
17 | @Input | |
| 19 | @Optional |
|
18 | @Optional | |
| 20 | public abstract Property<String> getTransport(); |
|
19 | public abstract Property<String> getTransport(); | |
| 21 |
|
20 | |||
| 22 | @Input |
|
21 | @Input | |
| 23 | @Optional |
|
22 | @Optional | |
| 24 | public abstract ListProperty<String> getOptions(); |
|
23 | public abstract ListProperty<String> getOptions(); | |
| 25 |
|
24 | |||
| 26 | @TaskAction |
|
25 | @TaskAction | |
| 27 | public void run() throws InterruptedException, IOException { |
|
26 | public void run() throws InterruptedException, IOException { | |
| 28 |
|
27 | |||
| 29 | docker().pushImage( |
|
28 | docker().pushImage( | |
| 30 |
getImageName().get() |
|
29 | getImageName().get(), | |
| 31 | getOptions().get()); |
|
30 | getOptions().get()); | |
| 32 | } |
|
31 | } | |
| 33 | } |
|
32 | } | |
| @@ -1,43 +1,42 | |||||
| 1 | package org.implab.gradle.containers.tasks; |
|
1 | package org.implab.gradle.containers.tasks; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 | import org.gradle.api.Action; |
|
4 | import org.gradle.api.Action; | |
| 5 | import org.gradle.api.provider.ListProperty; |
|
5 | import org.gradle.api.provider.ListProperty; | |
| 6 | import org.gradle.api.provider.Property; |
|
6 | import org.gradle.api.provider.Property; | |
| 7 | import org.gradle.api.tasks.Input; |
|
7 | import org.gradle.api.tasks.Input; | |
| 8 | import org.gradle.api.tasks.Optional; |
|
8 | import org.gradle.api.tasks.Optional; | |
| 9 | import org.implab.gradle.containers.cli.ImageName; |
|
|||
| 10 | import org.implab.gradle.containers.dsl.OptionsMixin; |
|
9 | import org.implab.gradle.containers.dsl.OptionsMixin; | |
| 11 | import org.implab.gradle.containers.dsl.VolumeSpec; |
|
10 | import org.implab.gradle.containers.dsl.VolumeSpec; | |
| 12 |
|
11 | |||
| 13 | public abstract class RunImage extends DockerCliTask implements OptionsMixin { |
|
12 | public abstract class RunImage extends DockerCliTask implements OptionsMixin { | |
| 14 |
|
13 | |||
| 15 | @Input |
|
14 | @Input | |
| 16 |
public abstract Property< |
|
15 | public abstract Property<String> getImageName(); | |
| 17 |
|
16 | |||
| 18 | @Input |
|
17 | @Input | |
| 19 | @Optional |
|
18 | @Optional | |
| 20 | public abstract ListProperty<String> getCommandLine(); |
|
19 | public abstract ListProperty<String> getCommandLine(); | |
| 21 |
|
20 | |||
| 22 |
|
21 | |||
| 23 | public void volume(Action<VolumeSpec> configure) { |
|
22 | public void volume(Action<VolumeSpec> configure) { | |
| 24 | getOptions().add("-v"); |
|
23 | getOptions().add("-v"); | |
| 25 | getOptions().add(provider(() -> { |
|
24 | getOptions().add(provider(() -> { | |
| 26 | var volumeSpec = getObjectFactory().newInstance(VolumeSpec.class); |
|
25 | var volumeSpec = getObjectFactory().newInstance(VolumeSpec.class); | |
| 27 | configure.execute(volumeSpec); |
|
26 | configure.execute(volumeSpec); | |
| 28 | return volumeSpec.resolveSpec(); |
|
27 | return volumeSpec.resolveSpec(); | |
| 29 | })); |
|
28 | })); | |
| 30 | } |
|
29 | } | |
| 31 |
|
30 | |||
| 32 | void commandLine(String ...args) { |
|
31 | void commandLine(String ...args) { | |
| 33 | getCommandLine().addAll(args); |
|
32 | getCommandLine().addAll(args); | |
| 34 | } |
|
33 | } | |
| 35 |
|
34 | |||
| 36 | public void run() throws InterruptedException, IOException { |
|
35 | public void run() throws InterruptedException, IOException { | |
| 37 | docker().runImage( |
|
36 | docker().runImage( | |
| 38 |
getImageName().get() |
|
37 | getImageName().get(), | |
| 39 | getOptions().get(), |
|
38 | getOptions().get(), | |
| 40 | getCommandLine().get()); |
|
39 | getCommandLine().get()); | |
| 41 | } |
|
40 | } | |
| 42 |
|
41 | |||
| 43 | } |
|
42 | } | |
| @@ -1,22 +1,21 | |||||
| 1 | package org.implab.gradle.containers.tasks; |
|
1 | package org.implab.gradle.containers.tasks; | |
| 2 |
|
2 | |||
| 3 | import java.io.IOException; |
|
3 | import java.io.IOException; | |
| 4 | import org.gradle.api.provider.Property; |
|
4 | import org.gradle.api.provider.Property; | |
| 5 | import org.gradle.api.tasks.Input; |
|
5 | import org.gradle.api.tasks.Input; | |
| 6 | import org.gradle.api.tasks.TaskAction; |
|
6 | import org.gradle.api.tasks.TaskAction; | |
| 7 | import org.implab.gradle.containers.cli.ImageName; |
|
|||
| 8 |
|
7 | |||
| 9 | public abstract class TagImage extends DockerCliTask { |
|
8 | public abstract class TagImage extends DockerCliTask { | |
| 10 | @Input |
|
9 | @Input | |
| 11 |
public abstract Property< |
|
10 | public abstract Property<String> getSrcImage(); | |
| 12 |
|
11 | |||
| 13 | @Input |
|
12 | @Input | |
| 14 |
public abstract Property< |
|
13 | public abstract Property<String> getDestImage(); | |
| 15 |
|
14 | |||
| 16 | @TaskAction |
|
15 | @TaskAction | |
| 17 | public void run() throws InterruptedException, IOException { |
|
16 | public void run() throws InterruptedException, IOException { | |
| 18 | docker().tagImage( |
|
17 | docker().tagImage( | |
| 19 |
getSrcImage( |
|
18 | getSrcImage().get(), | |
| 20 |
getDestImage( |
|
19 | getDestImage().get()); | |
| 21 | } |
|
20 | } | |
| 22 | } |
|
21 | } | |
General Comments 0
You need to be logged in to leave comments.
Login now
