Skip to content

Commit

Permalink
feat(announce): Add tread support to Twitter. Resolves jreleaser#853
Browse files Browse the repository at this point in the history
  • Loading branch information
diakogiannis committed Jul 16, 2022
1 parent 0002e62 commit e67536c
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@
*/
package org.jreleaser.model;

import org.jreleaser.bundle.RB;
import org.jreleaser.util.Env;
import org.jreleaser.util.JReleaserException;

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.jreleaser.util.Constants.HIDE;
import static org.jreleaser.util.Constants.UNSET;
import static org.jreleaser.util.Constants.*;
import static org.jreleaser.util.MustacheUtils.applyTemplate;
import static org.jreleaser.util.MustacheUtils.applyTemplates;
import static org.jreleaser.util.StringUtils.isNotBlank;
import static org.jreleaser.util.Templates.resolveTemplate;
Expand All @@ -44,6 +51,10 @@ public class Twitter extends AbstractAnnouncer<Twitter> {
private String accessTokenSecret;
private String status;

private final List<String> statuses = new ArrayList<>();

private String statusTemplate;

public Twitter() {
super(NAME);
}
Expand All @@ -57,6 +68,8 @@ public void merge(Twitter twitter) {
this.accessToken = merge(this.accessToken, twitter.accessToken);
this.accessTokenSecret = merge(this.accessTokenSecret, twitter.accessTokenSecret);
this.status = merge(this.status, twitter.status);
setStatuses(merge(this.statuses,twitter.statuses));
this.statusTemplate = merge(this.statusTemplate,twitter.statusTemplate);
}

public String getResolvedStatus(JReleaserContext context) {
Expand All @@ -66,6 +79,23 @@ public String getResolvedStatus(JReleaserContext context) {
return resolveTemplate(status, props);
}

public String getResolvedStatusTemplate(JReleaserContext context, Map<String, Object> extraProps) {
Map<String, Object> props = context.fullProps();
applyTemplates(props, getResolvedExtraProperties());
props.put(KEY_TAG_NAME, context.getModel().getRelease().getGitService()
.getEffectiveTagName(context.getModel()));
props.putAll(extraProps);

Path templatePath = context.getBasedir().resolve(statusTemplate);
try {
Reader reader = java.nio.file.Files.newBufferedReader(templatePath);
return applyTemplate(reader, props);
} catch (IOException e) {
throw new JReleaserException(RB.$("ERROR_unexpected_error_reading_template",
context.relativizeToBasedir(templatePath)));
}
}

public String getResolvedConsumerKey() {
return Env.env(TWITTER_CONSUMER_KEY, consumerKey);
}
Expand Down Expand Up @@ -127,12 +157,33 @@ public void setStatus(String status) {
this.status = status;
}

public void setStatuses(List<String> statuses){
freezeCheck();
this.statuses.clear();
this.statuses.addAll(statuses);
}

public List<String> getStatuses() {
return freezeWrap(statuses);
}

public String getStatusTemplate() {
return statusTemplate;
}

public void setStatusTemplate(String statusTemplate) {
freezeCheck();
this.statusTemplate = statusTemplate;
}

@Override
protected void asMap(Map<String, Object> props, boolean full) {
props.put("consumerKey", isNotBlank(getResolvedConsumerKey()) ? HIDE : UNSET);
props.put("consumerSecret", isNotBlank(getResolvedConsumerSecret()) ? HIDE : UNSET);
props.put("accessToken", isNotBlank(getResolvedAccessToken()) ? HIDE : UNSET);
props.put("accessTokenSecret", isNotBlank(getResolvedAccessTokenSecret()) ? HIDE : UNSET);
props.put("status", status);
props.put("statuses",statuses);
props.put("statusTemplate",statusTemplate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
import org.jreleaser.model.Twitter;
import org.jreleaser.util.Errors;

import java.nio.file.Files;

import static org.jreleaser.model.Twitter.TWITTER_ACCESS_TOKEN;
import static org.jreleaser.model.Twitter.TWITTER_ACCESS_TOKEN_SECRET;
import static org.jreleaser.model.Twitter.TWITTER_CONSUMER_KEY;
import static org.jreleaser.model.Twitter.TWITTER_CONSUMER_SECRET;
import static org.jreleaser.util.StringUtils.isBlank;
import static org.jreleaser.util.StringUtils.isNotBlank;

/**
* @author Andres Almiray
Expand Down Expand Up @@ -69,7 +72,12 @@ public static void validateTwitter(JReleaserContext context, Twitter twitter, Er
errors,
context.isDryrun()));

if (isBlank(twitter.getStatus())) {
if (isNotBlank(twitter.getStatusTemplate()) &&
!Files.exists(context.getBasedir().resolve(twitter.getStatusTemplate().trim()))) {
errors.configuration(RB.$("validation_directory_not_exist", "twitter.statusTemplate", twitter.getStatusTemplate()));
}

if (isBlank(twitter.getStatus()) && isBlank(twitter.getStatusTemplate()) && twitter.getStatuses().isEmpty()) {
twitter.setStatus(RB.$("default.release.message"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.jreleaser.gradle.plugin.dsl

import groovy.transform.CompileStatic
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property

/**
Expand All @@ -36,4 +37,10 @@ interface Twitter extends Announcer {
Property<String> getAccessTokenSecret()

Property<String> getStatus()

Property<String> getStatusTemplate()

ListProperty<String> getStatuses()

void status(String message)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ 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.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import org.jreleaser.gradle.plugin.dsl.Twitter

import javax.inject.Inject

import static org.jreleaser.util.StringUtils.isNotBlank

/**
*
* @author Andres Almiray
Expand All @@ -38,6 +41,8 @@ class TwitterImpl extends AbstractAnnouncer implements Twitter {
final Property<String> accessToken
final Property<String> accessTokenSecret
final Property<String> status
final Property<String> statusTemplate
final ListProperty<String> statuses

@Inject
TwitterImpl(ObjectFactory objects) {
Expand All @@ -47,6 +52,8 @@ class TwitterImpl extends AbstractAnnouncer implements Twitter {
accessToken = objects.property(String).convention(Providers.notDefined())
accessTokenSecret = objects.property(String).convention(Providers.notDefined())
status = objects.property(String).convention(Providers.notDefined())
statusTemplate = objects.property(String).convention(Providers.notDefined())
statuses = objects.listProperty(String).convention(Providers.notDefined())
}

@Override
Expand All @@ -57,7 +64,16 @@ class TwitterImpl extends AbstractAnnouncer implements Twitter {
consumerSecret.present ||
accessToken.present ||
accessTokenSecret.present ||
status.present
status.present ||
statusTemplate.present ||
statuses.present
}

@Override
void status(String message) {
if(isNotBlank(message)){
statuses.add(message.trim())
}
}

org.jreleaser.model.Twitter toModel() {
Expand All @@ -68,6 +84,8 @@ class TwitterImpl extends AbstractAnnouncer implements Twitter {
if (accessToken.present) twitter.accessToken = accessToken.get()
if (accessTokenSecret.present) twitter.accessTokenSecret = accessTokenSecret.get()
if (status.present) twitter.status = status.get()
if (statusTemplate.present) twitter.statusTemplate = statusTemplate.get()
twitter.statuses = (List<String>) statuses.getOrElse([])
twitter
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@

import org.jreleaser.bundle.RB;
import org.jreleaser.util.JReleaserLogger;
import twitter4j.Status;
import twitter4j.StatusUpdate;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

import java.util.List;

import static java.util.Objects.requireNonNull;
import static org.jreleaser.util.StringUtils.requireNonBlank;

Expand Down Expand Up @@ -67,8 +71,15 @@ public Twitter(JReleaserLogger logger,
this.logger.debug(RB.$("workflow.dryrun"), dryrun);
}

public void updateStatus(String status) throws TwitterException {
wrap(() -> twitter.updateStatus(status));
public void updateStatus(List<String> statuses) throws TwitterException {
wrap(() -> {
String message = statuses.get(0);
Status status = twitter.updateStatus(message);
for (int i = 1; i < statuses.size(); i++) {
status = twitter.updateStatus(new StatusUpdate(statuses.get(i))
.inReplyToStatusId(status.getId()));
}
});
}

private void wrap(TwitterOperation op) throws TwitterException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
import org.jreleaser.model.Twitter;
import org.jreleaser.model.announcer.spi.AnnounceException;
import org.jreleaser.model.announcer.spi.Announcer;
import org.jreleaser.util.Constants;
import org.jreleaser.util.MustacheUtils;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.jreleaser.util.Constants.KEY_TAG_NAME;
import static org.jreleaser.util.MustacheUtils.applyTemplates;
import static org.jreleaser.util.StringUtils.isNotBlank;
import static org.jreleaser.util.Templates.resolveTemplate;

/**
* @author Andres Almiray
Expand Down Expand Up @@ -49,9 +61,27 @@ public boolean isEnabled() {
public void announce() throws AnnounceException {
Twitter twitter = context.getModel().getAnnounce().getTwitter();

String status = twitter.getResolvedStatus(context);
context.getLogger().info(RB.$("twitter.tweet"), status);
context.getLogger().debug(RB.$("twitter.tweet.size"), status.length());
List<String> statuses = new ArrayList<>();

if (isNotBlank(twitter.getStatusTemplate())) {
Map<String, Object> props = new LinkedHashMap<>();
props.put(Constants.KEY_CHANGELOG, MustacheUtils.passThrough(context.getChangelog()));
context.getModel().getRelease().getGitService().fillProps(props, context.getModel());
statuses.add(twitter.getResolvedStatusTemplate(context,props));
}
if (statuses.isEmpty() && !twitter.getStatuses().isEmpty()) {
statuses.addAll(twitter.getStatuses());
}
if (statuses.isEmpty()) {
statuses.add(RB.$("default.release.message"));
}

for (int i = 0; i < statuses.size(); i++) {
String status = getResolvedMessage(context, statuses.get(i));
context.getLogger().info(RB.$("twitter.tweet"), status);
context.getLogger().debug(RB.$("twitter.tweet.size"), status.length());
statuses.set(i, status);
}

try {
UpdateStatusTwitterCommand.builder(context.getLogger())
Expand All @@ -61,12 +91,19 @@ public void announce() throws AnnounceException {
.consumerToken(context.isDryrun() ? "**UNDEFINED**" : twitter.getResolvedConsumerSecret())
.accessToken(context.isDryrun() ? "**UNDEFINED**" : twitter.getResolvedAccessToken())
.accessTokenSecret(context.isDryrun() ? "**UNDEFINED**" : twitter.getResolvedAccessTokenSecret())
.status(status)
.statuses(statuses)
.dryrun(context.isDryrun())
.build()
.execute();
} catch (TwitterException e) {
throw new AnnounceException(e);
}
}

private String getResolvedMessage(JReleaserContext context, String message) {
Map<String, Object> props = context.fullProps();
applyTemplates(props, context.getModel().getAnnounce().getTwitter().getResolvedExtraProperties());
props.put(KEY_TAG_NAME, context.getModel().getRelease().getGitService().getEffectiveTagName(context.getModel()));
return resolveTemplate(message, props);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@

import org.jreleaser.util.JReleaserLogger;

import java.util.List;

import static org.jreleaser.util.ObjectUtils.requireNonEmpty;
import static org.jreleaser.util.StringUtils.requireNonBlank;

/**
* @author Andres Almiray
* @since 0.1.0
*/
public class UpdateStatusTwitterCommand extends AbstractTwitterCommand {
private final String status;
private final List<String> statuses;

private UpdateStatusTwitterCommand(JReleaserLogger logger,
String apiHost,
Expand All @@ -37,35 +40,35 @@ private UpdateStatusTwitterCommand(JReleaserLogger logger,
String accessToken,
String accessTokenSecret,
boolean dryrun,
String status) {
List<String> statuses) {
super(logger, apiHost, connectTimeout, readTimeout, consumerKey, consumerToken, accessToken, accessTokenSecret, dryrun);
this.status = status;
this.statuses = statuses;
}

@Override
public void execute() throws TwitterException {
twitter.updateStatus(status);
twitter.updateStatus(statuses);
}

public static Builder builder(JReleaserLogger logger) {
return new Builder(logger);
}

public static class Builder extends AbstractTwitterCommand.Builder<Builder> {
private String status;
private List<String> statuses;

protected Builder(JReleaserLogger logger) {
super(logger);
}

public Builder status(String status) {
this.status = requireNonBlank(status, "'status' must not be blank");
public Builder statuses(List<String> statuses) {
this.statuses = (List<String>) requireNonEmpty(statuses, "'statuses' must not be empty");
return this;
}

public UpdateStatusTwitterCommand build() {
validate();
requireNonBlank(status, "'status' must not be blank");
this.statuses = (List<String>) requireNonEmpty(statuses, "'statuses' must not be empty");

return new UpdateStatusTwitterCommand(
logger,
Expand All @@ -77,7 +80,7 @@ public UpdateStatusTwitterCommand build() {
accessToken,
accessTokenSecret,
dryrun,
status);
statuses);
}
}
}

0 comments on commit e67536c

Please sign in to comment.