[changelog] let contributors be formatted. Resolves #306

This commit is contained in:
Andres Almiray
2021-07-24 15:33:01 +02:00
parent f99514ee1f
commit 2f724e1998
26 changed files with 713 additions and 22 deletions

View File

@@ -17,6 +17,7 @@
*/
package org.jreleaser.engine.changelog;
import org.jreleaser.engine.release.Releasers;
import org.jreleaser.model.JReleaserContext;
import org.jreleaser.model.JReleaserException;
import org.jreleaser.sdk.git.ChangelogProvider;
@@ -32,7 +33,7 @@ import java.nio.file.Path;
public class Changelog {
public static String createChangelog(JReleaserContext context) {
try {
return ChangelogProvider.getChangelog(context).trim();
return ChangelogProvider.getChangelog(context, Releasers.releaserFor(context)).trim();
} catch (IOException e) {
throw new JReleaserException("Unexpected error when creating changelog.", e);
}

View File

@@ -46,6 +46,7 @@ public class Changelog implements Domain, EnabledAware {
private final Set<Replacer> replacers = new LinkedHashSet<>();
private final Set<Labeler> labelers = new LinkedHashSet<>();
private final Hide hide = new Hide();
private final Contributors contributors = new Contributors();
private Boolean enabled;
private boolean links;
@@ -71,6 +72,7 @@ public class Changelog implements Domain, EnabledAware {
setReplacers(changelog.replacers);
setLabelers(changelog.labelers);
setHide(changelog.hide);
setContributors(changelog.contributors);
}
public boolean resolveFormatted(Project project) {
@@ -232,6 +234,14 @@ public class Changelog implements Domain, EnabledAware {
this.hide.setAll(hide);
}
public Contributors getContributors() {
return contributors;
}
public void setContributors(Contributors contributors) {
this.contributors.setAll(contributors);
}
@Deprecated
public void setHideUncategorized(boolean hideUncategorized) {
System.out.println("changelog.hideUncategorized has been deprecated since 0.6.0 and will be removed in the future. Use changelog.hide.uncategorized instead");
@@ -254,6 +264,7 @@ public class Changelog implements Domain, EnabledAware {
map.put("includeLabels", includeLabels);
map.put("excludeLabels", excludeLabels);
map.put("hide", hide.asMap(full));
map.put("contributors", contributors.asMap(full));
Map<String, Map<String, Object>> m = new LinkedHashMap<>();
int i = 0;
@@ -417,6 +428,44 @@ public class Changelog implements Domain, EnabledAware {
}
}
public static class Contributors implements Domain {
private Boolean enabled;
private String format;
void setAll(Contributors contributor) {
this.enabled = contributor.enabled;
this.format = contributor.format;
}
public boolean isEnabled() {
return enabled != null && enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabledSet() {
return enabled != null;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
@Override
public Map<String, Object> asMap(boolean full) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("enabled", enabled);
map.put("format", format);
return map;
}
}
public static class Hide implements Domain {
private final Set<String> categories = new LinkedHashSet<>();
private final Set<String> contributors = new LinkedHashSet<>();

View File

@@ -18,6 +18,7 @@
package org.jreleaser.model.releaser.spi;
import java.io.IOException;
import java.util.Optional;
/**
* @author Andres Almiray
@@ -27,4 +28,6 @@ public interface Releaser {
void release() throws ReleaseException;
Repository maybeCreateRepository(String owner, String repo, String password) throws IOException;
Optional<User> findUser(String email, String name);
}

View File

@@ -64,8 +64,8 @@ public class Repository {
"kind='" + kind + '\'' +
", owner='" + owner + '\'' +
", name='" + name + '\'' +
", url=" + url +
", httpUrl=" + httpUrl +
", url='" + url + '\'' +
", httpUrl'=" + httpUrl + '\'' +
"]";
}

View File

@@ -0,0 +1,76 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 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.releaser.spi;
import java.util.Objects;
/**
* @author Andres Almiray
* @since 0.1.0
*/
public class User {
private final String username;
private final String email;
private final String url;
public User(String username, String email, String url) {
this.username = username;
this.email = email;
this.url = url;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
public String getUrl() {
return url;
}
public String asLink(String input) {
return "[" + input + "](" + url + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return username.equals(user.username) &&
email.equals(user.email) &&
url.equals(user.url);
}
@Override
public int hashCode() {
return Objects.hash(username, email, url);
}
@Override
public String toString() {
return "User[" +
"username='" + username + '\'' +
", email='" + email + '\'' +
", url='" + url + '\'' +
"]";
}
}

View File

@@ -274,5 +274,9 @@ public abstract class GitServiceValidator extends Validator {
i++;
}
}
if (!changelog.getContributors().isEnabledSet()) {
changelog.getContributors().setEnabled(true);
}
}
}

View File

@@ -73,10 +73,16 @@ interface Changelog {
Hide getHide()
Contributors getContributors()
void hide(Action<? super Hide> action)
void contributors(Action<? super Contributors> action)
void hide(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Hide) Closure<Void> action)
void contributors(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Contributors) Closure<Void> action)
interface Category {
Property<String> getTitle()
@@ -97,6 +103,12 @@ interface Changelog {
Property<String> getReplace()
}
interface Contributors {
Property<Boolean> getEnabled()
Property<String> getFormat()
}
interface Hide {
Property<Boolean> getUncategorized()

View File

@@ -52,6 +52,7 @@ class ChangelogImpl implements Changelog {
final SetProperty<String> includeLabels
final SetProperty<String> excludeLabels
final HideImpl hide
final ContributorsImpl contributors
private final List<CategoryImpl> categories = []
private final Set<LabelerImpl> labelers = []
@@ -73,6 +74,7 @@ class ChangelogImpl implements Changelog {
includeLabels = objects.setProperty(String).convention(Providers.notDefined())
excludeLabels = objects.setProperty(String).convention(Providers.notDefined())
hide = objects.newInstance(HideImpl, objects)
contributors = objects.newInstance(ContributorsImpl, objects)
}
@Override
@@ -98,6 +100,7 @@ class ChangelogImpl implements Changelog {
!categories.isEmpty() ||
!labelers.isEmpty() ||
!replacers.isEmpty() ||
contributors.isSet()||
hide.isSet()
}
@@ -146,6 +149,11 @@ class ChangelogImpl implements Changelog {
action.execute(hide)
}
@Override
void contributors(Action<? super Contributors> action) {
action.execute(contributors)
}
@Override
void category(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Category) Closure<Void> action) {
CategoryImpl category = objects.newInstance(CategoryImpl, objects)
@@ -172,6 +180,11 @@ class ChangelogImpl implements Changelog {
ConfigureUtil.configure(action, hide)
}
@Override
void contributors(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Contributors) Closure<Void> action) {
ConfigureUtil.configure(action, contributors)
}
org.jreleaser.model.Changelog toModel() {
org.jreleaser.model.Changelog changelog = new org.jreleaser.model.Changelog()
if (enabled.present) {
@@ -201,6 +214,7 @@ class ChangelogImpl implements Changelog {
replacer.toModel()
} as Set<org.jreleaser.model.Changelog.Replacer>)
changelog.hide = hide.toModel()
changelog.contributors = contributors.toModel()
changelog
}
@@ -264,6 +278,31 @@ class ChangelogImpl implements Changelog {
}
}
@CompileStatic
static class ContributorsImpl implements Contributors {
final Property<Boolean> enabled
final Property<String> format
@Inject
ContributorsImpl(ObjectFactory objects) {
enabled = objects.property(Boolean).convention(Providers.notDefined())
format = objects.property(String).convention(Providers.notDefined())
}
@Internal
boolean isSet() {
enabled.present ||
format.present
}
org.jreleaser.model.Changelog.Contributors toModel() {
org.jreleaser.model.Changelog.Contributors contributors = new org.jreleaser.model.Changelog.Contributors()
if (enabled.present) contributors.enabled = enabled.get()
if (format.present) contributors.format = format.get()
contributors
}
}
@CompileStatic
static class HideImpl implements Hide {
final Property<Boolean> uncategorized

View File

@@ -40,6 +40,7 @@ public class Changelog implements EnabledAware {
private final List<Category> categories = new ArrayList<>();
private final Set<Replacer> replacers = new LinkedHashSet<>();
private final Set<Labeler> labelers = new LinkedHashSet<>();
private final Contributors contributors = new Contributors();
private final Hide hide = new Hide();
private Boolean enabled;
@@ -65,6 +66,7 @@ public class Changelog implements EnabledAware {
setCategories(changelog.categories);
setReplacers(changelog.replacers);
setLabelers(changelog.labelers);
setContributors(changelog.contributors);
setHide(changelog.hide);
}
@@ -212,6 +214,15 @@ public class Changelog implements EnabledAware {
this.hide.setUncategorized(hideUncategorized);
}
public Contributors getContributors() {
return contributors;
}
public void setContributors(Contributors hide) {
this.contributors.setAll(contributors);
}
public Hide getHide() {
return hide;
}
@@ -351,6 +362,36 @@ public class Changelog implements EnabledAware {
}
}
public static class Contributors {
private Boolean enabled;
private String format;
void setAll(Contributors contributor) {
this.enabled = contributor.enabled;
this.format = contributor.format;
}
public boolean isEnabled() {
return enabled != null && enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabledSet() {
return enabled != null;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
public static class Hide {
private final Set<String> categories = new LinkedHashSet<>();
private final Set<String> contributors = new LinkedHashSet<>();

View File

@@ -276,6 +276,7 @@ public final class JReleaserModelConverter {
c.setLabelers(convertLabelers(changelog.getLabelers()));
c.setReplacers(convertReplacers(changelog.getReplacers()));
c.setHide(convertHide(changelog.getHide()));
c.setContributors(convertContributors(changelog.getContributors()));
return c;
}
@@ -287,6 +288,13 @@ public final class JReleaserModelConverter {
return h;
}
private static org.jreleaser.model.Changelog.Contributors convertContributors(Changelog.Contributors contributors) {
org.jreleaser.model.Changelog.Contributors c = new org.jreleaser.model.Changelog.Contributors();
if (contributors.isEnabledSet()) c.setEnabled(contributors.isEnabled());
c.setFormat(contributors.getFormat());
return c;
}
private static List<org.jreleaser.model.Changelog.Category> convertCategories(List<Changelog.Category> categories) {
List<org.jreleaser.model.Changelog.Category> list = new ArrayList<>();
for (Changelog.Category category : categories) {

View File

@@ -22,11 +22,13 @@ import org.jreleaser.model.JReleaserContext;
import org.jreleaser.model.releaser.spi.ReleaseException;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.git.GitSdk;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
/**
* @author Andres Almiray
@@ -99,4 +101,9 @@ public class GenericGitReleaser implements Releaser {
public Repository maybeCreateRepository(String owner, String repo, String password) throws IOException {
return null;
}
@Override
public Optional<User> findUser(String email, String name) {
return Optional.empty();
}
}

View File

@@ -27,6 +27,8 @@ import org.jreleaser.model.Changelog;
import org.jreleaser.model.GitService;
import org.jreleaser.model.Gitlab;
import org.jreleaser.model.JReleaserContext;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.util.CollectionUtils;
import org.jreleaser.util.JavaModuleVersion;
import org.jreleaser.util.Version;
@@ -38,6 +40,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
@@ -46,6 +49,7 @@ import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static java.lang.System.lineSeparator;
import static java.util.stream.Collectors.groupingBy;
import static org.jreleaser.sdk.git.GitSdk.extractTagName;
import static org.jreleaser.util.ComparatorUtils.lessThan;
import static org.jreleaser.util.Constants.KEY_CHANGELOG_CHANGES;
@@ -63,15 +67,15 @@ import static org.jreleaser.util.StringUtils.toSafeRegexPattern;
public class ChangelogGenerator {
private static final String UNCATEGORIZED = "<<UNCATEGORIZED>>";
public static String generate(JReleaserContext context) throws IOException {
public static String generate(JReleaserContext context, Releaser releaser) throws IOException {
if (!context.getModel().getRelease().getGitService().getChangelog().isEnabled()) {
return "";
}
return createChangelog(context);
return createChangelog(context, releaser);
}
private static String createChangelog(JReleaserContext context) throws IOException {
private static String createChangelog(JReleaserContext context, Releaser releaser) throws IOException {
GitService gitService = context.getModel().getRelease().getGitService();
Changelog changelog = gitService.getChangelog();
@@ -93,7 +97,7 @@ public class ChangelogGenerator {
context.getLogger().debug("sorting commits {}", changelog.getSort());
if (changelog.resolveFormatted(context.getModel().getProject())) {
return formatChangelog(context, changelog, commits, revCommitComparator, commitSeparator);
return formatChangelog(context, releaser, changelog, commits, revCommitComparator, commitSeparator);
}
String commitsUrl = gitService.getResolvedCommitUrl(context.getModel());
@@ -255,20 +259,26 @@ public class ChangelogGenerator {
}
private static String formatChangelog(JReleaserContext context,
Releaser releaser,
Changelog changelog,
Iterable<RevCommit> commits,
Comparator<RevCommit> revCommitComparator,
String lineSeparator) {
Set<String> contributorNames = new LinkedHashSet<>();
Set<Contributor> contributors = new LinkedHashSet<>();
Map<String, List<Commit>> categories = new LinkedHashMap<>();
StreamSupport.stream(commits.spliterator(), false)
.sorted(revCommitComparator)
.map(Commit::of)
.peek(c -> {
if (!changelog.getHide().containsContributor(c.author)) contributorNames.add(c.author);
if (isNotBlank(c.committer) && !changelog.getHide().containsContributor(c.committer))
contributorNames.add(c.committer);
if (!changelog.getContributors().isEnabled()) return;
if (!changelog.getHide().containsContributor(c.author)) {
contributors.add(new Contributor(c.author, c.authorEmail));
}
if (isNotBlank(c.committer) && !changelog.getHide().containsContributor(c.committer)) {
contributors.add(new Contributor(c.committer, c.committerEmail));
}
})
.peek(c -> applyLabels(c, changelog.getLabelers()))
.filter(c -> checkLabels(c, changelog))
@@ -309,18 +319,57 @@ public class ChangelogGenerator {
.append(lineSeparator());
}
StringBuilder contributors = new StringBuilder("## Contributors")
.append(lineSeparator)
.append(String.join(", ", contributorNames))
.append(lineSeparator);
StringBuilder formattedContributors = new StringBuilder();
if (changelog.getContributors().isEnabled()) {
formattedContributors.append("## Contributors")
.append(lineSeparator)
.append("We'd like to thank the following people for their contributions:")
.append(lineSeparator)
.append(formatContributors(context, releaser, changelog, contributors, lineSeparator))
.append(lineSeparator);
}
Map<String, Object> props = context.props();
props.put(KEY_CHANGELOG_CHANGES, passThrough(changes.toString()));
props.put(KEY_CHANGELOG_CONTRIBUTORS, passThrough(contributors.toString()));
props.put(KEY_CHANGELOG_CONTRIBUTORS, passThrough(formattedContributors.toString()));
return applyReplacers(context, changelog, stripMargin(applyTemplate(changelog.getResolvedContentTemplate(context), props)));
}
private static String formatContributors(JReleaserContext context,
Releaser releaser,
Changelog changelog,
Set<Contributor> contributors,
String lineSeparator) {
List<String> list = new ArrayList<>();
String format = changelog.getContributors().getFormat();
Map<String, List<Contributor>> grouped = contributors.stream()
.peek(contributor -> {
if (isNotBlank(format) && (format.contains("AsLink") || format.contains("Username"))) {
releaser.findUser(contributor.email, contributor.name)
.ifPresent(contributor::setUser);
}
})
.collect(groupingBy(Contributor::getName));
String contributorFormat = isNotBlank(format) ? format : "{{contributorName}}";
grouped.forEach((name, cs) -> {
Optional<Contributor> contributor = cs.stream()
.filter(c -> c.getUser() != null)
.findFirst();
if (contributor.isPresent()) {
list.add(applyTemplate(contributorFormat, contributor.get().asContext()));
} else {
list.add(applyTemplate(contributorFormat, cs.get(0).asContext()));
}
});
String separator = contributorFormat.startsWith("-") || contributorFormat.startsWith("*") ? lineSeparator : ", ";
return String.join(separator, list);
}
private static String applyReplacers(JReleaserContext context, Changelog changelog, String text) {
Map<String, Object> props = context.getModel().props();
context.getModel().getRelease().getGitService().fillProps(props, context.getModel());
@@ -387,6 +436,8 @@ public class ChangelogGenerator {
private String body;
private String author;
private String committer;
private String authorEmail;
private String committerEmail;
private int time;
Map<String, Object> asContext(boolean links, String commitsUrl) {
@@ -411,8 +462,74 @@ public class ChangelogGenerator {
c.title = c.body.split(lineSeparator())[0];
c.author = rc.getAuthorIdent().getName();
c.committer = rc.getCommitterIdent().getName();
c.authorEmail = rc.getAuthorIdent().getEmailAddress();
c.committerEmail = rc.getCommitterIdent().getEmailAddress();
c.time = rc.getCommitTime();
return c;
}
}
private static class Contributor implements Comparable<Contributor> {
private final String name;
private final String email;
private User user;
private Contributor(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Map<String, Object> asContext() {
Map<String, Object> context = new LinkedHashMap<>();
context.put("contributorName", passThrough(name));
context.put("contributorNameAsLink", passThrough(name));
context.put("contributorUsername", "");
context.put("contributorUsernameAsLink", "");
if (user != null) {
context.put("contributorNameAsLink", passThrough(user.asLink(name)));
context.put("contributorUsername", passThrough(user.getUsername()));
context.put("contributorUsernameAsLink", passThrough(user.asLink("@" + user.getUsername())));
}
return context;
}
@Override
public int compareTo(Contributor that) {
return email.compareTo(that.email);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Contributor that = (Contributor) o;
return email.equals(that.email);
}
@Override
public int hashCode() {
return Objects.hash(email);
}
@Override
public String toString() {
return name + " <" + email + ">";
}
}
}

View File

@@ -19,6 +19,7 @@ package org.jreleaser.sdk.git;
import org.jreleaser.model.Changelog;
import org.jreleaser.model.JReleaserContext;
import org.jreleaser.model.releaser.spi.Releaser;
import java.io.File;
import java.io.IOException;
@@ -36,8 +37,8 @@ import static org.jreleaser.util.StringUtils.isNotBlank;
* @since 0.1.0
*/
public class ChangelogProvider {
public static String getChangelog(JReleaserContext context) throws IOException {
String content = resolveChangelog(context);
public static String getChangelog(JReleaserContext context, Releaser releaser) throws IOException {
String content = resolveChangelog(context, releaser);
Path changelogFile = context.getOutputDirectory()
.resolve("release")
@@ -52,7 +53,7 @@ public class ChangelogProvider {
return content;
}
private static String resolveChangelog(JReleaserContext context) throws IOException {
private static String resolveChangelog(JReleaserContext context, Releaser releaser) throws IOException {
Changelog changelog = context.getModel().getRelease().getGitService().getChangelog();
if (!changelog.isEnabled()) {
@@ -72,6 +73,6 @@ public class ChangelogProvider {
return new String(Files.readAllBytes(externalChangelogPath));
}
return ChangelogGenerator.generate(context);
return ChangelogGenerator.generate(context, releaser);
}
}

View File

@@ -29,6 +29,7 @@ import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.commons.ClientUtils;
import org.jreleaser.sdk.commons.RestAPIException;
import org.jreleaser.sdk.gitea.api.GiteaAPI;
@@ -36,6 +37,8 @@ import org.jreleaser.sdk.gitea.api.GtMilestone;
import org.jreleaser.sdk.gitea.api.GtOrganization;
import org.jreleaser.sdk.gitea.api.GtRelease;
import org.jreleaser.sdk.gitea.api.GtRepository;
import org.jreleaser.sdk.gitea.api.GtSearchUser;
import org.jreleaser.sdk.gitea.api.GtUser;
import org.jreleaser.util.CollectionUtils;
import org.jreleaser.util.JReleaserLogger;
@@ -222,6 +225,18 @@ class Gitea {
}
}
Optional<User> findUser(String email, String name, String host) throws RestAPIException {
logger.debug("looking up user for {} <{}>", name, email);
GtSearchUser search = api.searchUser(CollectionUtils.<String, String>newMap("q", email));
if (null != search.getData() && !search.getData().isEmpty()) {
GtUser user = search.getData().get(0);
return Optional.of(new User(user.getUsername(), email, host + user.getUsername()));
}
return Optional.empty();
}
private FormData toFormData(Path asset) throws IOException {
return FormData.builder()
.fileName(asset.getFileName().toString())

View File

@@ -22,6 +22,7 @@ import org.jreleaser.model.UpdateSection;
import org.jreleaser.model.releaser.spi.ReleaseException;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.commons.RestAPIException;
import org.jreleaser.sdk.git.GitSdk;
import org.jreleaser.sdk.gitea.api.GtMilestone;
@@ -151,6 +152,36 @@ public class GiteaReleaser implements Releaser {
repository.getCloneUrl());
}
@Override
public Optional<User> findUser(String email, String name) {
org.jreleaser.model.Gitea gitea = resolveGiteaFromModel();
try {
String host = gitea.getHost();
String endpoint = gitea.getApiEndpoint();
if (endpoint.startsWith("https")) {
host = "https://" + host;
} else {
host = "http://" + host;
}
if (!host.endsWith("/")) {
host += "/";
}
return new Gitea(context.getLogger(),
gitea.getApiEndpoint(),
gitea.getResolvedToken(),
gitea.getConnectTimeout(),
gitea.getReadTimeout())
.findUser(email, name, host);
} catch (IOException e) {
context.getLogger().trace(e);
context.getLogger().debug("Could not find user matching {}", email);
}
return Optional.empty();
}
private void createRelease(Gitea api, String tagName, String changelog, boolean deleteTags) throws IOException {
org.jreleaser.model.Gitea gitea = resolveGiteaFromModel();

View File

@@ -19,6 +19,7 @@ package org.jreleaser.sdk.gitea.api;
import feign.Headers;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;
import feign.form.FormData;
@@ -70,4 +71,8 @@ public interface GiteaAPI {
@RequestLine("PATCH /repos/{owner}/{repo}/milestones/{id}")
@Headers("Content-Type: application/json")
void updateMilestone(Map<String, Object> params, @Param("owner") String owner, @Param("repo") String repo, @Param("id") Integer id);
@RequestLine("GET /users/search")
@Headers("Content-Type: application/json")
GtSearchUser searchUser(@QueryMap Map<String, String> q);
}

View File

@@ -0,0 +1,39 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 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.sdk.gitea.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List;
/**
* @author Andres Almiray
* @since 0.6.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GtSearchUser {
private List<GtUser> data;
public List<GtUser> getData() {
return data;
}
public void setData(List<GtUser> data) {
this.data = data;
}
}

View File

@@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 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.sdk.gitea.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Andres Almiray
* @since 0.6.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GtUser {
private String id;
private String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@@ -22,6 +22,7 @@ import org.jreleaser.model.UpdateSection;
import org.jreleaser.model.releaser.spi.ReleaseException;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.git.GitSdk;
import org.jreleaser.sdk.github.api.GhRelease;
import org.kohsuke.github.GHMilestone;
@@ -142,6 +143,25 @@ public class GithubReleaser implements Releaser {
repository.getHttpTransportUrl());
}
@Override
public Optional<User> findUser(String email, String name) {
org.jreleaser.model.Github github = context.getModel().getRelease().getGithub();
try {
return new XGithub(context.getLogger(),
github.getApiEndpoint(),
github.getResolvedToken(),
github.getConnectTimeout(),
github.getReadTimeout())
.findUser(email, name);
} catch (IOException e) {
context.getLogger().trace(e);
context.getLogger().debug("Could not find user matching {}", email);
}
return Optional.empty();
}
private void createRelease(Github api, String tagName, String changelog, boolean deleteTags) throws IOException {
org.jreleaser.model.Github github = context.getModel().getRelease().getGithub();

View File

@@ -27,13 +27,18 @@ import feign.httpclient.ApacheHttpClient;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.apache.tika.Tika;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.commons.ClientUtils;
import org.jreleaser.sdk.commons.RestAPIException;
import org.jreleaser.sdk.github.api.GhRelease;
import org.jreleaser.sdk.github.api.GhSearchUser;
import org.jreleaser.sdk.github.api.GhUser;
import org.jreleaser.sdk.github.api.GithubAPI;
import org.jreleaser.util.CollectionUtils;
import org.jreleaser.util.JReleaserLogger;
import java.io.IOException;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
import static org.jreleaser.util.StringUtils.requireNonBlank;
@@ -78,4 +83,24 @@ class XGithub {
api.updateRelease(release, owner, repo, id);
}
Optional<User> findUser(String email, String name) throws RestAPIException {
logger.debug("looking up user for {} <{}>", name, email);
GhSearchUser search = api.searchUser(CollectionUtils.<String, String>newMap("q", email));
if (search.getTotalCount() > 0) {
GhUser user = search.getItems().get(0);
return Optional.of(new User(user.getLogin(), email, user.getHtmlUrl()));
}
// use full name instead
String query = "fullname:" + name + " type:user";
search = api.searchUser(CollectionUtils.<String, String>newMap("q", query));
if (search.getTotalCount() > 0) {
GhUser user = search.getItems().get(0);
return Optional.of(new User(user.getLogin(), email, user.getHtmlUrl()));
}
return Optional.empty();
}
}

View File

@@ -0,0 +1,48 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 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.sdk.github.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List;
/**
* @author Andres Almiray
* @since 0.6.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GhSearchUser {
private int totalCount = 0;
private List<GhUser> items;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public List<GhUser> getItems() {
return items;
}
public void setItems(List<GhUser> items) {
this.items = items;
}
}

View File

@@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 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.sdk.github.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Andres Almiray
* @since 0.6.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GhUser {
private String id;
private String login;
private String htmlUrl;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getHtmlUrl() {
return htmlUrl;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
}

View File

@@ -19,8 +19,11 @@ package org.jreleaser.sdk.github.api;
import feign.Headers;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;
import java.util.Map;
/**
* @author Andres Almiray
* @since 0.6.0
@@ -29,4 +32,8 @@ public interface GithubAPI {
@RequestLine("PATCH /repos/{owner}/{repo}/releases/{id}")
@Headers("Content-Type: application/json")
void updateRelease(GhRelease release, @Param("owner") String owner, @Param("repo") String repo, @Param("id") Long id);
@RequestLine("GET /search/users")
@Headers("Content-Type: application/json")
GhSearchUser searchUser(@QueryMap Map<String, String> q);
}

View File

@@ -162,13 +162,13 @@ class Gitlab {
if (StringUtils.isNotBlank(identifier)) {
logger.debug("fetching project with GitLab id {}", identifier);
project = api.getProject(identifier.trim());
project = api.getProject(identifier.trim());
} else {
User u = getCurrentUser();
logger.debug("fetching project {} for user {} ({})", projectName, u.getUsername(), u.getId());
List<Project> projects = api.getProject(u.getId(), CollectionUtils.<String, Object>map()
.e("search", projectName));
.e("search", projectName));
if (projects == null || projects.isEmpty()) {
throw new RestAPIException(404, "Project " + projectName + " does not exist or it's not visible");
@@ -274,6 +274,24 @@ class Gitlab {
}
}
Optional<org.jreleaser.model.releaser.spi.User> findUser(String email, String name) throws RestAPIException {
logger.debug("looking up user for {} <{}>", name, email);
List<User> users = api.searchUser(CollectionUtils.<String, String>newMap("scope", "users", "search", email));
if (users != null && !users.isEmpty()) {
User user = users.get(0);
return Optional.of(new org.jreleaser.model.releaser.spi.User(user.getUsername(), email, user.getWebUrl()));
}
users = api.searchUser(CollectionUtils.<String, String>newMap("scope", "users", "search", name));
if (users != null && !users.isEmpty()) {
User user = users.get(0);
return Optional.of(new org.jreleaser.model.releaser.spi.User(user.getUsername(), email, user.getWebUrl()));
}
return Optional.empty();
}
private FormData toFormData(Path asset) throws IOException {
return FormData.builder()
.fileName(asset.getFileName().toString())

View File

@@ -22,6 +22,7 @@ import org.jreleaser.model.UpdateSection;
import org.jreleaser.model.releaser.spi.ReleaseException;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.model.releaser.spi.User;
import org.jreleaser.sdk.commons.RestAPIException;
import org.jreleaser.sdk.git.GitSdk;
import org.jreleaser.sdk.gitlab.api.FileUpload;
@@ -152,6 +153,25 @@ public class GitlabReleaser implements Releaser {
project.getHttpUrlToRepo());
}
@Override
public Optional<User> findUser(String email, String name) {
org.jreleaser.model.Gitlab gitlab = context.getModel().getRelease().getGitlab();
try {
return new Gitlab(context.getLogger(),
gitlab.getApiEndpoint(),
gitlab.getResolvedToken(),
gitlab.getConnectTimeout(),
gitlab.getReadTimeout())
.findUser(email, name);
} catch (IOException e) {
context.getLogger().trace(e);
context.getLogger().debug("Could not find user matching {}", email);
}
return Optional.empty();
}
private void createRelease(Gitlab api, String tagName, String changelog, boolean deleteTags) throws IOException {
org.jreleaser.model.Gitlab gitlab = context.getModel().getRelease().getGitlab();

View File

@@ -76,4 +76,8 @@ public interface GitlabAPI {
@RequestLine("PUT /projects/{projectId}/milestones/{milestoneId}")
@Headers("Content-Type: application/json")
void updateMilestone(Map<String, Object> params, @Param("projectId") Integer projectId, @Param("milestoneId") Integer milestoneId);
@RequestLine("GET /search")
@Headers("Content-Type: application/json")
List<User> searchUser(@QueryMap Map<String, String> q);
}