From 2261f8d1ee4af2ee653f99aa7d82946d932066a0 Mon Sep 17 00:00:00 2001 From: Andres Almiray Date: Thu, 11 Aug 2022 19:00:41 +0200 Subject: [PATCH] feat(core): Add a screenshots section to project. Resolves #883 --- .../java/org/jreleaser/model/Project.java | 26 ++++ .../java/org/jreleaser/model/Screenshot.java | 133 ++++++++++++++++++ .../model/validation/ProjectValidator.java | 5 + .../jreleaser/model/validation/Validator.java | 36 +++++ .../org/jreleaser/bundle/Messages.properties | 1 + .../gradle/plugin/dsl/Project.groovy | 4 + .../gradle/plugin/dsl/Screenshot.groovy | 43 ++++++ .../plugin/internal/dsl/ProjectImpl.groovy | 27 ++++ .../plugin/internal/dsl/ScreenshotImpl.groovy | 73 ++++++++++ 9 files changed, 348 insertions(+) create mode 100644 core/jreleaser-model/src/main/java/org/jreleaser/model/Screenshot.java create mode 100644 plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Screenshot.groovy create mode 100644 plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ScreenshotImpl.groovy diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/Project.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/Project.java index c6dbb301b..8321bfa80 100644 --- a/core/jreleaser-model/src/main/java/org/jreleaser/model/Project.java +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/Project.java @@ -63,6 +63,7 @@ public class Project extends AbstractModelObject implements Domain, Ext private final Links links = new Links(); private final Java java = new Java(); private final Snapshot snapshot = new Snapshot(); + private final List screenshots = new ArrayList<>(); private String name; private String version; private VersionPattern versionPattern = new VersionPattern(); @@ -81,6 +82,7 @@ public void freeze() { java.freeze(); snapshot.freeze(); versionPattern.freeze(); + screenshots.forEach(ModelObject::freeze); } @Override @@ -102,6 +104,7 @@ public void merge(Project project) { setTags(merge(this.tags, project.tags)); setExtraProperties(merge(this.extraProperties, project.extraProperties)); setLinks(project.links); + setScreenshots(merge(this.screenshots, project.screenshots)); } @Override @@ -277,6 +280,23 @@ public void setStereotype(String str) { setStereotype(Stereotype.of(str)); } + public List getScreenshots() { + return freezeWrap(screenshots); + } + + public void setScreenshots(List screenshots) { + freezeCheck(); + this.screenshots.clear(); + this.screenshots.addAll(screenshots); + } + + public void addScreenshot(Screenshot screenshot) { + freezeCheck(); + if (null != screenshot) { + this.screenshots.add(screenshot); + } + } + public Java getJava() { return java; } @@ -348,6 +368,12 @@ public Map asMap(boolean full) { map.put("tags", tags); map.put("stereotype", stereotype); map.put("links", links.asMap(full)); + Map> sm = new LinkedHashMap<>(); + int i = 0; + for (Screenshot screenshot : screenshots) { + sm.put("screenshot " + (i++), screenshot.asMap(full)); + } + map.put("screenshots", sm); map.put("extraProperties", getResolvedExtraProperties()); if (java.isEnabled()) { map.put("java", java.asMap(full)); diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/Screenshot.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/Screenshot.java new file mode 100644 index 000000000..9a8f90910 --- /dev/null +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/Screenshot.java @@ -0,0 +1,133 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 The JReleaser authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jreleaser.model; + +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +import static org.jreleaser.util.StringUtils.isBlank; + +/** + * @author Andres Almiray + * @since 1.2.0 + */ +public class Screenshot extends AbstractModelObject implements Domain { + private Type type = Type.SOURCE; + private Boolean primary; + private String url; + private String caption; + private Integer width; + private Integer height; + + @Override + public void merge(Screenshot registry) { + freezeCheck(); + this.type = merge(this.type, registry.type); + this.primary = merge(this.primary, registry.primary); + this.url = merge(this.url, registry.url); + this.caption = merge(this.caption, registry.caption); + this.width = merge(this.width, registry.width); + this.height = merge(this.height, registry.height); + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + freezeCheck(); + this.type = type; + } + + public void setType(String str) { + setType(Type.of(str)); + } + + public Boolean isPrimary() { + return primary != null && primary; + } + + public void setPrimary(Boolean primary) { + freezeCheck(); + this.primary = primary; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + freezeCheck(); + this.url = url; + } + + public String getCaption() { + return caption; + } + + public void setCaption(String caption) { + freezeCheck(); + this.caption = caption; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + freezeCheck(); + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + freezeCheck(); + this.height = height; + } + + @Override + public Map asMap(boolean full) { + Map map = new LinkedHashMap<>(); + map.put("type", type); + map.put("url", url); + if (isPrimary()) map.put("primary", isPrimary()); + map.put("caption", caption); + map.put("width", width); + map.put("height", height); + return map; + } + + public enum Type { + SOURCE, + THUMBNAIL; + + @Override + public String toString() { + return name().toLowerCase(Locale.ENGLISH); + } + + public static Type of(String str) { + if (isBlank(str)) return null; + return Type.valueOf(str.toUpperCase(Locale.ENGLISH).trim()); + } + } +} diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/ProjectValidator.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/ProjectValidator.java index 9f25c2c43..7a101eb48 100644 --- a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/ProjectValidator.java +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/ProjectValidator.java @@ -23,9 +23,12 @@ import org.jreleaser.model.JReleaserContext; import org.jreleaser.model.LicenseId; import org.jreleaser.model.Project; +import org.jreleaser.model.Screenshot; import org.jreleaser.model.VersionPattern; import org.jreleaser.util.Errors; +import java.util.List; + import static org.jreleaser.model.Project.DEFAULT_SNAPSHOT_LABEL; import static org.jreleaser.model.Project.DEFAULT_SNAPSHOT_PATTERN; import static org.jreleaser.model.Project.PROJECT_NAME; @@ -111,6 +114,8 @@ public static void validateProject(JReleaserContext context, JReleaserContext.Mo if ((mode.validateConfig() && javaDistributions) || javaAssemblers) { validateJava(context, project, errors); } + + validateScreenshots(context, mode, project.getScreenshots(), errors, "project"); } public static void postValidateProject(JReleaserContext context, JReleaserContext.Mode mode, Errors errors) { diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/Validator.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/Validator.java index 4a0ca62aa..8ce4362d5 100644 --- a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/Validator.java +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/Validator.java @@ -30,11 +30,13 @@ import org.jreleaser.model.OwnerAware; import org.jreleaser.model.Packager; import org.jreleaser.model.RepositoryTap; +import org.jreleaser.model.Screenshot; import org.jreleaser.model.TimeoutAware; import org.jreleaser.util.Env; import org.jreleaser.util.Errors; import java.util.Collection; +import java.util.List; import static org.jreleaser.util.StringUtils.isBlank; import static org.jreleaser.util.StringUtils.isNotBlank; @@ -246,4 +248,38 @@ static void validateFileSet(JReleaserContext context, JReleaserContext.Mode mode errors.configuration(RB.$("validation_must_not_be_null", assembler.getType() + "." + assembler.getName() + ".fileSet[" + index + "].input")); } } + + static void validateScreenshots(JReleaserContext context, JReleaserContext.Mode mode, List screenshots, Errors errors, String base) { + if (screenshots.size() == 1) { + screenshots.get(0).setPrimary(true); + } + + if (screenshots.stream() + .mapToInt(s -> s.isPrimary() ? 1 : 0) + .sum() > 1) { + errors.configuration(RB.$("validation_multiple_primary_screenshots", base)); + } + + for (int i = 0; i < screenshots.size(); i++) { + Screenshot screenshot = screenshots.get(i); + if (isBlank(screenshot.getUrl())) { + errors.configuration(RB.$("validation_must_not_be_blank", base + ".screenshots[" + i + "].url")); + } + + if (screenshot.getType() == Screenshot.Type.THUMBNAIL) { + if (null == screenshot.getWidth()) { + errors.configuration(RB.$("validation_must_not_be_null", base + ".screenshots[" + i + "].width")); + } + if (null == screenshot.getHeight()) { + errors.configuration(RB.$("validation_must_not_be_null", base + ".screenshots[" + i + "].height")); + } + } else { + if (null == screenshot.getWidth() && null != screenshot.getHeight()) { + errors.configuration(RB.$("validation_must_not_be_null", base + ".screenshots[" + i + "].width")); + } else if (null != screenshot.getWidth() && null == screenshot.getHeight()) { + errors.configuration(RB.$("validation_must_not_be_null", base + ".screenshots[" + i + "].height")); + } + } + } + } } diff --git a/core/jreleaser-utils/src/main/resources/org/jreleaser/bundle/Messages.properties b/core/jreleaser-utils/src/main/resources/org/jreleaser/bundle/Messages.properties index f635b6641..f8f8971e0 100644 --- a/core/jreleaser-utils/src/main/resources/org/jreleaser/bundle/Messages.properties +++ b/core/jreleaser-utils/src/main/resources/org/jreleaser/bundle/Messages.properties @@ -262,6 +262,7 @@ ERROR_artifacts_unexpected_error_globs = Unexpected error when resolving globs ERROR_artifacts_unexpected_error_path = Unexpected error visiting path {} ERROR_artifacts_download_url_missing = cannot resolve downloadUrl for {}. Using default settings from {} +validation_multiple_primary_screenshots = There's more than 1 primary screenshot in {} validation_java_home_missing = Java home could not be found validation_s3_missing_download_url = {} defines a custom endpoint but no downloadUrl validation_gitlab_non_matching_uploader = Uploader {}:{} is not configured diff --git a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Project.groovy b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Project.groovy index 0752e990c..ede5ca468 100644 --- a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Project.groovy +++ b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Project.groovy @@ -87,6 +87,10 @@ interface Project extends ExtraProperties { void snapshot(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Snapshot) Closure action) + void screenshot(Action action) + + void screenshot(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Screenshot) Closure action) + interface Snapshot { Property getPattern() diff --git a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Screenshot.groovy b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Screenshot.groovy new file mode 100644 index 000000000..554092993 --- /dev/null +++ b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/dsl/Screenshot.groovy @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 The JReleaser authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jreleaser.gradle.plugin.dsl + +import groovy.transform.CompileStatic +import org.gradle.api.provider.Property + +/** + * + * @author Andres Almiray + * @since 1.2.0 + */ +@CompileStatic +interface Screenshot { + void setScreenshotType(String str) + + Property getScreenshotType() + + Property getPrimary() + + Property getUrl() + + Property getCaption() + + Property getWidth() + + Property getHeight() +} \ No newline at end of file diff --git a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ProjectImpl.groovy b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ProjectImpl.groovy index e4b0c97f7..51dbeb02d 100644 --- a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ProjectImpl.groovy +++ b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ProjectImpl.groovy @@ -19,6 +19,8 @@ package org.jreleaser.gradle.plugin.internal.dsl import groovy.transform.CompileStatic import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.NamedDomainObjectFactory import org.gradle.api.internal.provider.Providers import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty @@ -28,6 +30,7 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal import org.jreleaser.gradle.plugin.dsl.Java import org.jreleaser.gradle.plugin.dsl.Project +import org.jreleaser.gradle.plugin.dsl.Screenshot import org.jreleaser.model.Stereotype import org.kordamp.gradle.util.ConfigureUtil @@ -62,6 +65,8 @@ class ProjectImpl implements Project { final SnapshotImpl snapshot final LinksImpl links + private final NamedDomainObjectContainer screenshots + @Inject ProjectImpl(ObjectFactory objects, Provider nameProvider, @@ -87,6 +92,15 @@ class ProjectImpl implements Project { java = objects.newInstance(JavaImpl, objects) snapshot = objects.newInstance(SnapshotImpl, objects) links = objects.newInstance(LinksImpl, objects) + + screenshots = objects.domainObjectContainer(ScreenshotImpl, new NamedDomainObjectFactory() { + @Override + ScreenshotImpl create(String name) { + ScreenshotImpl screenshot = objects.newInstance(ScreenshotImpl, objects) + screenshot.name = name + screenshot + } + }) } @Override @@ -140,6 +154,16 @@ class ProjectImpl implements Project { ConfigureUtil.configure(action, snapshot) } + @Override + void screenshot(Action action) { + action.execute(screenshots.maybeCreate("screenshot-${screenshots.size()}".toString())) + } + + @Override + void screenshot(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Screenshot) Closure action) { + ConfigureUtil.configure(action, screenshots.maybeCreate("screenshot-${screenshots.size()}".toString())) + } + org.jreleaser.model.Project toModel() { org.jreleaser.model.Project project = new org.jreleaser.model.Project() project.name = name.get() @@ -161,6 +185,9 @@ class ProjectImpl implements Project { project.java = java.toModel() project.snapshot = snapshot.toModel() project.links = links.toModel() + for (ScreenshotImpl screenshot : screenshots) { + project.addScreenshot(screenshot.toModel()) + } project } diff --git a/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ScreenshotImpl.groovy b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ScreenshotImpl.groovy new file mode 100644 index 000000000..15d50aabb --- /dev/null +++ b/plugins/jreleaser-gradle-plugin/src/main/groovy/org/jreleaser/gradle/plugin/internal/dsl/ScreenshotImpl.groovy @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2022 The JReleaser authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jreleaser.gradle.plugin.internal.dsl + +import groovy.transform.CompileStatic +import org.gradle.api.internal.provider.Providers +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.jreleaser.gradle.plugin.dsl.Screenshot + +import javax.inject.Inject + +import static org.jreleaser.util.StringUtils.isNotBlank + +/** + * + * @author Andres Almiray + * @since 1.2.0 + */ +@CompileStatic +class ScreenshotImpl implements Screenshot { + String name + final Property screenshotType + final Property primary + final Property url + final Property caption + final Property width + final Property height + + @Inject + ScreenshotImpl(ObjectFactory objects) { + screenshotType = objects.property(org.jreleaser.model.Screenshot.Type) + .convention(org.jreleaser.model.Screenshot.Type.SOURCE) + primary = objects.property(Boolean).convention(Providers.notDefined()) + url = objects.property(String).convention(Providers.notDefined()) + caption = objects.property(String).convention(Providers.notDefined()) + width = objects.property(Integer).convention(Providers.notDefined()) + height = objects.property(Integer).convention(Providers.notDefined()) + } + + @Override + void setScreenshotType(String str) { + if (isNotBlank(str)) { + this.screenshotType.set(org.jreleaser.model.Screenshot.Type.of(str.trim())) + } + } + + org.jreleaser.model.Screenshot toModel() { + org.jreleaser.model.Screenshot screenshot = new org.jreleaser.model.Screenshot() + screenshot.type = screenshotType.get() + if (primary.present) screenshot.primary = primary.get() + if (url.present) screenshot.url = url.get() + if (caption.present) screenshot.caption = caption.get() + if (width.present) screenshot.width = width.get() + if (height.present) screenshot.height = height.get() + screenshot + } +}