Support closing milestones upon release

This commit is contained in:
Andres Almiray
2021-04-07 18:12:42 +02:00
parent c07f71ee69
commit c197ffb9a7
29 changed files with 627 additions and 42 deletions

View File

@@ -85,6 +85,8 @@ public class Signer {
List<FilePair> files = collectArtifacts(context, keyring);
if (files.isEmpty()) {
context.getLogger().info("No files configured for signing. Skipping");
context.getLogger().restorePrefix();
context.getLogger().decreaseIndent();
return;
}
@@ -94,6 +96,8 @@ public class Signer {
if (files.isEmpty()) {
context.getLogger().info("All signatures are up-to-date and valid. Skipping");
context.getLogger().restorePrefix();
context.getLogger().decreaseIndent();
return;
}

View File

@@ -40,6 +40,9 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
private static final String TAG_EARLY_ACCESS = "early-access";
private final String serviceName;
private final Changelog changelog = new Changelog();
private final Milestone milestone = new Milestone();
private final CommitAuthor commitAuthor = new CommitAuthor();
protected Boolean enabled;
private String host;
private String owner;
@@ -54,10 +57,8 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
private String token;
private String tagName = "v{{projectVersion}}";
private String releaseName = "Release {{tagName}}";
private CommitAuthor commitAuthor = new CommitAuthor();
private boolean sign;
private boolean skipTagging;
private Changelog changelog = new Changelog();
private boolean overwrite;
private boolean allowUploadToExisting;
private String apiEndpoint;
@@ -94,16 +95,9 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
this.overwrite = service.overwrite;
this.allowUploadToExisting = service.allowUploadToExisting;
this.apiEndpoint = service.apiEndpoint;
this.commitAuthor.setAll(service.commitAuthor);
this.changelog.setAll(service.changelog);
}
public void setCachedTagName(String cachedTagName) {
this.cachedTagName = cachedTagName;
}
public void setCachedReleaseName(String cachedReleaseName) {
this.cachedReleaseName = cachedReleaseName;
setCommitAuthor(service.commitAuthor);
setChangelog(service.changelog);
setMilestone(service.milestone);
}
public String getCanonicalRepoName() {
@@ -125,6 +119,8 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
if (isBlank(cachedTagName)) {
cachedTagName = applyTemplate(new StringReader(tagName), props(project));
} else if (cachedTagName.contains("{{")) {
cachedTagName = applyTemplate(new StringReader(cachedTagName), props(project));
}
return cachedTagName;
@@ -135,7 +131,7 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
return TAG_EARLY_ACCESS;
}
return getResolvedTagName(project);
return cachedTagName;
}
public String getResolvedReleaseName(Project project) {
@@ -145,11 +141,17 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
if (isBlank(cachedReleaseName)) {
cachedReleaseName = applyTemplate(new StringReader(releaseName), props(project));
} else if (cachedReleaseName.contains("{{")) {
cachedReleaseName = applyTemplate(new StringReader(cachedReleaseName), props(project));
}
return cachedReleaseName;
}
public String getEffectiveReleaseName() {
return cachedReleaseName;
}
public String getResolvedRepoUrl(Project project) {
return applyTemplate(new StringReader(repoUrlFormat), props(project));
}
@@ -306,7 +308,7 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
@Override
public void setCommitAuthor(CommitAuthor commitAuthor) {
this.commitAuthor = commitAuthor;
this.commitAuthor.setAll(commitAuthor);
}
public boolean isSign() {
@@ -330,7 +332,15 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
}
public void setChangelog(Changelog changelog) {
this.changelog = changelog;
this.changelog.setAll(changelog);
}
public Milestone getMilestone() {
return milestone;
}
public void setMilestone(Milestone milestone) {
this.milestone.setAll(milestone);
}
public boolean isOverwrite() {
@@ -381,11 +391,11 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
map.put("allowUploadToExisting", allowUploadToExisting);
map.put("apiEndpoint", apiEndpoint);
map.put("changelog", changelog.asMap());
map.put("milestone", milestone.asMap());
return map;
}
private Map<String, Object> props(Project project) {
public Map<String, Object> props(Project project) {
// duplicate from JReleaserModel to avoid endless recursion
Map<String, Object> props = new LinkedHashMap<>();
props.put(Constants.KEY_PROJECT_NAME, project.getName());
@@ -412,6 +422,7 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
props.put(Constants.KEY_CANONICAL_REPO_NAME, getCanonicalRepoName());
props.put(Constants.KEY_TAG_NAME, project.isSnapshot() ? TAG_EARLY_ACCESS : cachedTagName);
props.put(Constants.KEY_RELEASE_NAME, cachedReleaseName);
props.put(Constants.KEY_MILESTONE_NAME, milestone.getEffectiveName());
return props;
}
@@ -423,7 +434,8 @@ public abstract class GitService implements Releaser, CommitAuthorProvider, Owne
props.put(Constants.KEY_REVERSE_REPO_HOST, getReverseRepoHost());
props.put(Constants.KEY_CANONICAL_REPO_NAME, getCanonicalRepoName());
props.put(Constants.KEY_TAG_NAME, getEffectiveTagName(project));
props.put(Constants.KEY_RELEASE_NAME, getResolvedReleaseName(project));
props.put(Constants.KEY_RELEASE_NAME, getEffectiveReleaseName());
props.put(Constants.KEY_MILESTONE_NAME, milestone.getEffectiveName());
props.put(Constants.KEY_REPO_URL, getResolvedRepoUrl(project));
props.put(Constants.KEY_COMMIT_URL, getResolvedCommitUrl(project));
props.put(Constants.KEY_RELEASE_NOTES_URL, getResolvedReleaseNotesUrl(project));

View File

@@ -212,7 +212,8 @@ public class JReleaserModel implements Domain {
props.put(Constants.KEY_REPO_NAME, service.getName());
props.put(Constants.KEY_REPO_BRANCH, service.getBranch());
props.put(Constants.KEY_TAG_NAME, service.getEffectiveTagName(project));
props.put(Constants.KEY_RELEASE_NAME, service.getResolvedReleaseName(project));
props.put(Constants.KEY_RELEASE_NAME, service.getEffectiveReleaseName());
props.put(Constants.KEY_MILESTONE_NAME, service.getMilestone().getEffectiveName());
props.put(Constants.KEY_REVERSE_REPO_HOST, service.getReverseRepoHost());
props.put(Constants.KEY_CANONICAL_REPO_NAME, service.getCanonicalRepoName());
props.put(Constants.KEY_REPO_URL, service.getResolvedRepoUrl(project));

View File

@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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 org.jreleaser.util.Env;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.jreleaser.util.MustacheUtils.applyTemplate;
import static org.jreleaser.util.StringUtils.isBlank;
/**
* @author Andres Almiray
* @since 0.1.0
*/
public class Milestone implements Domain {
public static final String MILESTONE_NAME = "MILESTONE_NAME";
private Boolean close;
private String name = "{{ tagName }}";
private String cachedName;
void setAll(Milestone changelog) {
this.close = changelog.close;
this.name = changelog.name;
}
public String getEffectiveName() {
return cachedName;
}
public String getResolvedName(Map<String, Object> props) {
if (isBlank(cachedName)) {
cachedName = Env.resolve(MILESTONE_NAME, cachedName);
}
if (isBlank(cachedName)) {
cachedName = applyTemplate(new StringReader(name), props);
} else if (cachedName.contains("{{")) {
cachedName = applyTemplate(new StringReader(cachedName), props);
}
return cachedName;
}
public Boolean isClose() {
return close == null || close;
}
public void setClose(Boolean close) {
this.close = close;
}
public boolean isCloseSet() {
return close != null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Object> asMap() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("name", name);
map.put("close", isClose());
return map;
}
}

View File

@@ -28,6 +28,7 @@ import java.util.List;
import static org.jreleaser.model.GitService.RELEASE_NAME;
import static org.jreleaser.model.GitService.TAG_NAME;
import static org.jreleaser.model.Milestone.MILESTONE_NAME;
import static org.jreleaser.util.StringUtils.isBlank;
/**
@@ -92,9 +93,22 @@ public abstract class GitServiceValidator extends Validator {
service.getCommitAuthor().setEmail("jreleaser-bot@jreleaser.org");
}
// milestone
service.getMilestone().setName(
checkProperty(context.getModel().getEnvironment(),
MILESTONE_NAME,
service.getServiceName() + ".milestone.name",
service.getMilestone().getName(),
errors));
if (isBlank(service.getMilestone().getName())) {
service.getMilestone().setName("{{ tagName }}");
}
// eager resolve
service.getResolvedTagName(project);
service.getResolvedReleaseName(project);
service.getMilestone().getResolvedName(service.props(project));
if (project.isSnapshot()) {
service.setReleaseName(StringUtils.capitalize(project.getName()) + " Early-Access");

View File

@@ -17,30 +17,32 @@
*/
package org.jreleaser.util;
import java.util.Stack;
/**
* @author Andres Almiray
* @since 0.1.0
*/
public abstract class AbstractJReleaserLogger implements JReleaserLogger {
private final Stack<String> prefix = new Stack<>();
private String indent = "";
private String prefix = null;
private String previousPrefix = null;
@Override
public void reset() {
this.prefix = this.previousPrefix = null;
this.prefix.clear();
this.indent = "";
}
@Override
public void setPrefix(String prefix) {
this.previousPrefix = this.prefix;
this.prefix = prefix;
this.prefix.push(prefix);
}
@Override
public void restorePrefix() {
this.prefix = this.previousPrefix;
if (!this.prefix.isEmpty()) {
this.prefix.pop();
}
}
@Override
@@ -56,6 +58,6 @@ public abstract class AbstractJReleaserLogger implements JReleaserLogger {
}
protected String formatMessage(String message) {
return indent + (prefix != null ? "[" + prefix + "] " : "") + message;
return indent + (!prefix.isEmpty() ? "[" + prefix.peek() + "] " : "") + message;
}
}

View File

@@ -58,6 +58,7 @@ public interface Constants {
String KEY_REPO_BRANCH = "repoBranch";
String KEY_TAG_NAME = "tagName";
String KEY_RELEASE_NAME = "releaseName";
String KEY_MILESTONE_NAME = "milestoneName";
String KEY_CANONICAL_REPO_NAME = "repoCanonicalName";
String KEY_REPO_URL = "repoUrl";
String KEY_COMMIT_URL = "commitsUrl";

View File

@@ -35,6 +35,7 @@ import java.util.Properties;
import static org.jreleaser.util.Constants.KEY_COMMIT_FULL_HASH;
import static org.jreleaser.util.Constants.KEY_COMMIT_SHORT_HASH;
import static org.jreleaser.util.Constants.KEY_MILESTONE_NAME;
import static org.jreleaser.util.Constants.KEY_PROJECT_SNAPSHOT;
import static org.jreleaser.util.Constants.KEY_PROJECT_VERSION;
import static org.jreleaser.util.Constants.KEY_RELEASE_NAME;
@@ -102,7 +103,8 @@ class WorkflowImpl implements Workflow {
props.put(KEY_PROJECT_VERSION, project.getResolvedVersion());
props.put(KEY_PROJECT_SNAPSHOT, String.valueOf(project.isSnapshot()));
props.put(KEY_TAG_NAME, model.getRelease().getGitService().getEffectiveTagName(project));
props.put(KEY_RELEASE_NAME, model.getRelease().getGitService().getResolvedReleaseName(project));
props.put(KEY_RELEASE_NAME, model.getRelease().getGitService().getEffectiveReleaseName());
props.put(KEY_MILESTONE_NAME, model.getRelease().getGitService().getMilestone().getEffectiveName());
Map<String, Object> resolvedExtraProperties = project.getResolvedExtraProperties();
safePut("project" + capitalize(KEY_VERSION_MAJOR), resolvedExtraProperties, props);

View File

@@ -66,6 +66,10 @@ interface GitService extends Releaser {
void changelog(Action<? super Changelog> action)
Milestone getMilestone()
void milestone(Action<? super Milestone> action)
CommitAuthor getCommitAuthor()
void commitAuthor(Action<? super CommitAuthor> action)

View File

@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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 0.1.0
*/
@CompileStatic
interface Milestone {
Property<Boolean> getClose()
Property<Boolean> getName()
}

View File

@@ -26,6 +26,7 @@ import org.gradle.api.tasks.Internal
import org.jreleaser.gradle.plugin.dsl.Changelog
import org.jreleaser.gradle.plugin.dsl.CommitAuthor
import org.jreleaser.gradle.plugin.dsl.GitService
import org.jreleaser.gradle.plugin.dsl.Milestone
import javax.inject.Inject
@@ -108,6 +109,11 @@ abstract class AbstractGitService implements GitService {
action.execute(changelog)
}
@Override
void milestone(Action<? super Milestone> action) {
action.execute(milestone)
}
@Override
void commitAuthor(Action<? super CommitAuthor> action) {
action.execute(commitAuthor)

View File

@@ -37,6 +37,7 @@ class GiteaImpl extends AbstractGitService implements Gitea {
final Property<Boolean> draft
final Property<Boolean> prerelease
final ChangelogImpl changelog
final MilestoneImpl milestone
final CommitAuthorImpl commitAuthor
@Inject
@@ -47,6 +48,7 @@ class GiteaImpl extends AbstractGitService implements Gitea {
prerelease = objects.property(Boolean).convention(Providers.notDefined())
changelog = objects.newInstance(ChangelogImpl, objects)
milestone = objects.newInstance(MilestoneImpl, objects)
commitAuthor = objects.newInstance(CommitAuthorImpl, objects)
}
@@ -57,6 +59,7 @@ class GiteaImpl extends AbstractGitService implements Gitea {
draft.present ||
prerelease.present ||
changelog.isSet() ||
milestone.isSet() ||
commitAuthor.isSet()
}
@@ -67,6 +70,7 @@ class GiteaImpl extends AbstractGitService implements Gitea {
service.draft = draft.getOrElse(false)
service.prerelease = prerelease.getOrElse(false)
if (changelog.isSet()) service.changelog = changelog.toModel()
if (milestone.isSet()) service.milestone = milestone.toModel()
if (commitAuthor.isSet()) service.commitAuthor = commitAuthor.toModel()
service
}

View File

@@ -37,6 +37,7 @@ class GithubImpl extends AbstractGitService implements Github {
final Property<Boolean> draft
final Property<Boolean> prerelease
final ChangelogImpl changelog
final MilestoneImpl milestone
final CommitAuthorImpl commitAuthor
@Inject
@@ -47,6 +48,7 @@ class GithubImpl extends AbstractGitService implements Github {
prerelease = objects.property(Boolean).convention(Providers.notDefined())
changelog = objects.newInstance(ChangelogImpl, objects)
milestone = objects.newInstance(MilestoneImpl, objects)
commitAuthor = objects.newInstance(CommitAuthorImpl, objects)
}
@@ -57,6 +59,7 @@ class GithubImpl extends AbstractGitService implements Github {
draft.present ||
prerelease.present ||
changelog.isSet() ||
milestone.isSet() ||
commitAuthor.isSet()
}
@@ -67,6 +70,7 @@ class GithubImpl extends AbstractGitService implements Github {
service.draft = draft.getOrElse(false)
service.prerelease = prerelease.getOrElse(false)
if (changelog.isSet()) service.changelog = changelog.toModel()
if (milestone.isSet()) service.milestone = milestone.toModel()
if (commitAuthor.isSet()) service.commitAuthor = commitAuthor.toModel()
service
}

View File

@@ -35,6 +35,7 @@ import javax.inject.Inject
class GitlabImpl extends AbstractGitService implements Gitlab {
final Property<String> ref
final ChangelogImpl changelog
final MilestoneImpl milestone
final CommitAuthorImpl commitAuthor
@Inject
@@ -43,6 +44,7 @@ class GitlabImpl extends AbstractGitService implements Gitlab {
ref = objects.property(String).convention(Providers.notDefined())
changelog = objects.newInstance(ChangelogImpl, objects)
milestone = objects.newInstance(MilestoneImpl, objects)
commitAuthor = objects.newInstance(CommitAuthorImpl, objects)
}
@@ -51,6 +53,7 @@ class GitlabImpl extends AbstractGitService implements Gitlab {
super.isSet() ||
ref.present ||
changelog.isSet() ||
milestone.isSet() ||
commitAuthor.isSet()
}
@@ -59,6 +62,7 @@ class GitlabImpl extends AbstractGitService implements Gitlab {
toModel(service)
if (ref.present) service.ref = ref.get()
if (changelog.isSet()) service.changelog = changelog.toModel()
if (milestone.isSet()) service.milestone = milestone.toModel()
if (commitAuthor.isSet()) service.commitAuthor = commitAuthor.toModel()
service
}

View File

@@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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.gradle.api.tasks.Internal
import org.jreleaser.gradle.plugin.dsl.Milestone
import javax.inject.Inject
/**
*
* @author Andres Almiray
* @since 0.1.0
*/
@CompileStatic
class MilestoneImpl implements Milestone {
final Property<Boolean> close
final Property<String> name
@Inject
MilestoneImpl(ObjectFactory objects) {
close = objects.property(Boolean).convention(Providers.notDefined())
name = objects.property(String).convention(Providers.notDefined())
}
@Internal
boolean isSet() {
close.present ||
name.present
}
org.jreleaser.model.Milestone toModel() {
org.jreleaser.model.Milestone milestone = new org.jreleaser.model.Milestone()
if (close.present) milestone.close = close.get()
if (name.present) milestone.name = name.get()
milestone
}
}

View File

@@ -22,6 +22,9 @@ package org.jreleaser.maven.plugin;
* @since 0.1.0
*/
public abstract class GitService implements Releaser {
private final CommitAuthor commitAuthor = new CommitAuthor();
private final Changelog changelog = new Changelog();
private final Milestone milestone = new Milestone();
protected Boolean enabled;
private String host;
private String owner;
@@ -36,10 +39,8 @@ public abstract class GitService implements Releaser {
private String token;
private String tagName;
private String releaseName;
private CommitAuthor commitAuthor = new CommitAuthor();
private boolean sign;
private boolean skipTagging;
private Changelog changelog = new Changelog();
private boolean overwrite;
private boolean allowUploadToExisting;
private String apiEndpoint;
@@ -59,13 +60,14 @@ public abstract class GitService implements Releaser {
this.token = service.token;
this.tagName = service.tagName;
this.releaseName = service.releaseName;
this.commitAuthor.setAll(service.commitAuthor);
this.sign = service.sign;
this.skipTagging = service.skipTagging;
this.overwrite = service.overwrite;
this.allowUploadToExisting = service.allowUploadToExisting;
this.apiEndpoint = service.apiEndpoint;
this.changelog.setAll(service.changelog);
setCommitAuthor(service.commitAuthor);
setChangelog(service.changelog);
setMilestone(service.milestone);
}
@Override
@@ -192,7 +194,7 @@ public abstract class GitService implements Releaser {
}
public void setCommitAuthor(CommitAuthor commitAuthor) {
this.commitAuthor = commitAuthor;
this.commitAuthor.setAll(commitAuthor);
}
public boolean isSign() {
@@ -216,7 +218,15 @@ public abstract class GitService implements Releaser {
}
public void setChangelog(Changelog changelog) {
this.changelog = changelog;
this.changelog.setAll(changelog);
}
public Milestone getMilestone() {
return milestone;
}
public void setMilestone(Milestone milestone) {
this.milestone.setAll(milestone);
}
public boolean isOverwrite() {

View File

@@ -0,0 +1,52 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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.maven.plugin;
/**
* @author Andres Almiray
* @since 0.1.0
*/
public class Milestone {
private Boolean close;
private String name;
void setAll(Milestone changelog) {
this.close = changelog.close;
this.name = changelog.name;
}
public Boolean isClose() {
return close == null || close;
}
public void setClose(Boolean close) {
this.close = close;
}
public boolean isCloseSet() {
return close != null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -38,6 +38,7 @@ import org.jreleaser.maven.plugin.Java;
import org.jreleaser.maven.plugin.Jbang;
import org.jreleaser.maven.plugin.Jreleaser;
import org.jreleaser.maven.plugin.Mail;
import org.jreleaser.maven.plugin.Milestone;
import org.jreleaser.maven.plugin.Packagers;
import org.jreleaser.maven.plugin.Plug;
import org.jreleaser.maven.plugin.Project;
@@ -176,6 +177,7 @@ public final class JReleaserModelConverter {
s.setAllowUploadToExisting(service.isAllowUploadToExisting());
s.setApiEndpoint(service.getApiEndpoint());
s.setChangelog(convertChangelog(service.getChangelog()));
s.setMilestone(convertMilestone(service.getMilestone()));
}
private static org.jreleaser.model.CommitAuthor convertCommitAuthor(CommitAuthor commitAuthor) {
@@ -193,6 +195,13 @@ public final class JReleaserModelConverter {
return c;
}
private static org.jreleaser.model.Milestone convertMilestone(Milestone milestone) {
org.jreleaser.model.Milestone m = new org.jreleaser.model.Milestone();
m.setClose(milestone.isClose());
m.setName(milestone.getName());
return m;
}
private static org.jreleaser.model.Packagers convertPackagers(Packagers packagers) {
org.jreleaser.model.Packagers p = new org.jreleaser.model.Packagers();
if (packagers.getBrew().isSet()) p.setBrew(convertBrew(packagers.getBrew()));

View File

@@ -26,6 +26,10 @@ dependencies {
api "io.github.openfeign:feign-core:$feignVersion"
api "io.github.openfeign:feign-jackson:$feignVersion"
api("io.github.openfeign:feign-httpclient:$feignVersion") {
exclude group: 'commons-logging', module: 'commons-logging'
}
api "org.slf4j:jcl-over-slf4j:$slf4jVersion"
api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion"
api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
api "io.github.openfeign.form:feign-form:$feignFormVersion"

View File

@@ -26,12 +26,14 @@ import feign.Feign;
import feign.Request;
import feign.form.FormData;
import feign.form.FormEncoder;
import feign.httpclient.ApacheHttpClient;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.jreleaser.sdk.gitea.api.GiteaAPI;
import org.jreleaser.sdk.gitea.api.GiteaAPIException;
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;
@@ -43,10 +45,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
import static org.jreleaser.util.StringUtils.isBlank;
import static org.jreleaser.util.StringUtils.requireNonBlank;
/**
@@ -80,6 +82,7 @@ class Gitea {
this.logger = logger;
this.api = Feign.builder()
.client(new ApacheHttpClient())
.encoder(new FormEncoder(new JacksonEncoder(objectMapper)))
.decoder(new JacksonDecoder(objectMapper))
.requestInterceptor(template -> template.header("Authorization", String.format("token %s", token)))
@@ -101,6 +104,33 @@ class Gitea {
}
}
Optional<GtMilestone> findMilestoneByName(String owner, String repo, String milestoneName) {
logger.debug("Lookup milestone '{}' on {}/{}", milestoneName, owner, repo);
try {
GtMilestone milestone = api.findMilestoneByTitle(owner, repo, milestoneName);
if (milestone == null) {
return Optional.empty();
}
return "open".equals(milestone.getState()) ? Optional.of(milestone) : Optional.empty();
} catch (GiteaAPIException e) {
if (e.isNotFound()) {
// ok
return Optional.empty();
}
throw e;
}
}
void closeMilestone(String owner, String repo, GtMilestone milestone) throws IOException {
logger.debug("Closing milestone '{}' on {}/{}", milestone.getTitle(), owner, repo);
api.updateMilestone(CollectionUtils.<String, Object>map()
.e("state", "closed"), owner, repo, milestone.getId());
}
GtRepository createRepository(String owner, String repo) {
logger.debug("Creating repository {}/{}", owner, repo);

View File

@@ -23,6 +23,7 @@ import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.sdk.git.GitSdk;
import org.jreleaser.sdk.gitea.api.GiteaAPIException;
import org.jreleaser.sdk.gitea.api.GtMilestone;
import org.jreleaser.sdk.gitea.api.GtRelease;
import org.jreleaser.sdk.gitea.api.GtRepository;
@@ -31,6 +32,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author Andres Almiray
@@ -123,20 +125,30 @@ public class GiteaReleaser implements Releaser {
}
// local tag
if (deleteTags || !context.getModel().getRelease().getGitService().isSkipTagging()) {
if (deleteTags || !gitea.isSkipTagging()) {
context.getLogger().debug("Tagging local repository with {}", tagName);
GitSdk.of(context).tag(tagName, true);
}
// remote tag/release
GtRelease release = new GtRelease();
release.setName(gitea.getResolvedReleaseName(context.getModel().getProject()));
release.setName(gitea.getEffectiveReleaseName());
release.setTagName(gitea.getEffectiveTagName(context.getModel().getProject()));
release.setTargetCommitish(gitea.getTargetCommitish());
release.setBody(changelog);
release = api.createRelease(gitea.getOwner(), gitea.getName(), release);
api.uploadAssets(gitea.getOwner(), gitea.getName(), release, assets);
Optional<GtMilestone> milestone = api.findMilestoneByName(
gitea.getOwner(),
gitea.getName(),
gitea.getMilestone().getEffectiveName());
if (milestone.isPresent()) {
api.closeMilestone(gitea.getOwner(),
gitea.getName(),
milestone.get());
}
}
private void deleteTags(Gitea api, String owner, String repo, String tagName) {

View File

@@ -37,11 +37,11 @@ public interface GiteaAPI {
@RequestLine("POST /orgs/{org}/repos")
@Headers("Content-Type: application/json")
GtRepository createRepository(Map<String,Object> data, @Param("org") String org);
GtRepository createRepository(Map<String, Object> data, @Param("org") String org);
@RequestLine("POST /user/repos")
@Headers("Content-Type: application/json")
GtRepository createRepository(Map<String,Object> data);
GtRepository createRepository(Map<String, Object> data);
@RequestLine("GET /repos/{owner}/{repo}/releases/tags/{tag}")
GtRelease getReleaseByTagName(@Param("owner") String owner, @Param("repo") String repo, @Param("tag") String tag);
@@ -59,4 +59,11 @@ public interface GiteaAPI {
@RequestLine("POST /repos/{owner}/{repo}/releases/{id}/assets")
@Headers("Content-Type: multipart/form-data")
GtAttachment uploadAsset(@Param("owner") String owner, @Param("repo") String repo, @Param("id") Integer id, @Param("attachment") FormData file);
@RequestLine("GET /repos/{owner}/{repo}/milestones/{milestoneName}")
GtMilestone findMilestoneByTitle(@Param("owner") String owner, @Param("repo") String repo, @Param("milestoneName") String milestoneName);
@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);
}

View File

@@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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.1.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class GtMilestone {
private Integer id;
private String title;
private String state;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}

View File

@@ -24,6 +24,8 @@ import org.kohsuke.github.GHAsset;
import org.kohsuke.github.GHDiscussion;
import org.kohsuke.github.GHException;
import org.kohsuke.github.GHFileNotFoundException;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHMilestone;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GHReleaseBuilder;
@@ -31,6 +33,7 @@ import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.kohsuke.github.PagedIterable;
import java.io.IOException;
import java.nio.file.Files;
@@ -93,6 +96,22 @@ class Github {
.create();
}
Optional<GHMilestone> findMilestoneByName(String owner, String repo, String milestoneName) throws IOException {
logger.debug("Lookup milestone '{}' on {}/{}", milestoneName, owner, repo);
GHRepository repository = findRepository(owner, repo);
PagedIterable<GHMilestone> milestones = repository.listMilestones(GHIssueState.OPEN);
return StreamSupport.stream(milestones.spliterator(), false)
.filter(m -> milestoneName.equals(m.getTitle()))
.findFirst();
}
void closeMilestone(String owner, String repo, GHMilestone milestone) throws IOException {
logger.debug("Closing milestone '{}' on {}/{}", milestone.getTitle(), owner, repo);
milestone.close();
}
GHRelease findReleaseByTag(String repo, String tagName) throws IOException {
logger.debug("Fetching release on {} with tag {}", repo, tagName);
return github.getRepository(repo)

View File

@@ -22,6 +22,7 @@ import org.jreleaser.model.releaser.spi.ReleaseException;
import org.jreleaser.model.releaser.spi.Releaser;
import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.sdk.git.GitSdk;
import org.kohsuke.github.GHMilestone;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GHRepository;
@@ -30,6 +31,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author Andres Almiray
@@ -120,7 +122,7 @@ public class GithubReleaser implements Releaser {
}
// local tag
if (deleteTags || !context.getModel().getRelease().getGitService().isSkipTagging()) {
if (deleteTags || !github.isSkipTagging()) {
context.getLogger().debug("Tagging local repository with {}", tagName);
GitSdk.of(context).tag(tagName, true);
}
@@ -129,12 +131,22 @@ public class GithubReleaser implements Releaser {
GHRelease release = api.createRelease(github.getCanonicalRepoName(),
github.getEffectiveTagName(context.getModel().getProject()))
.commitish(github.getTargetCommitish())
.name(github.getResolvedReleaseName(context.getModel().getProject()))
.name(github.getEffectiveReleaseName())
.draft(github.isDraft())
.prerelease(github.isPrerelease())
.body(changelog)
.create();
api.uploadAssets(release, assets);
Optional<GHMilestone> milestone = api.findMilestoneByName(
github.getOwner(),
github.getName(),
github.getMilestone().getEffectiveName());
if (milestone.isPresent()) {
api.closeMilestone(github.getOwner(),
github.getName(),
milestone.get());
}
}
private void deleteTags(Github api, String repo, String tagName) {

View File

@@ -33,6 +33,7 @@ import org.apache.tika.mime.MediaType;
import org.jreleaser.sdk.gitlab.api.FileUpload;
import org.jreleaser.sdk.gitlab.api.GitlabAPI;
import org.jreleaser.sdk.gitlab.api.GitlabAPIException;
import org.jreleaser.sdk.gitlab.api.Milestone;
import org.jreleaser.sdk.gitlab.api.Project;
import org.jreleaser.sdk.gitlab.api.Release;
import org.jreleaser.sdk.gitlab.api.User;
@@ -44,6 +45,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static java.util.Objects.requireNonNull;
@@ -114,6 +116,40 @@ class Gitlab {
return projects.get(0);
}
Optional<Milestone> findMilestoneByName(String owner, String repo, String milestoneName) throws IOException {
logger.debug("Lookup milestone '{}' on {}/{}", milestoneName, owner, repo);
Project project = getProject(repo);
try {
List<Milestone> milestones = api.findMilestoneByTitle(project.getId(), CollectionUtils.<String, Object>map()
.e("title", milestoneName));
if (milestones == null || milestones.isEmpty()) {
return Optional.empty();
}
Milestone milestone = milestones.get(0);
return "active".equals(milestone.getState()) ? Optional.of(milestone) : Optional.empty();
} catch (GitlabAPIException e) {
if (e.isNotFound() || e.isForbidden()) {
// ok
return Optional.empty();
}
throw e;
}
}
void closeMilestone(String owner, String repo, Milestone milestone) throws IOException {
logger.debug("Closing milestone '{}' on {}/{}", milestone.getTitle(), owner, repo);
Project project = getProject(repo);
api.updateMilestone(CollectionUtils.<String, Object>map()
.e("state_event", "close"),
project.getId(), milestone.getId());
}
Project createProject(String owner, String repo) throws IOException {
logger.debug("Creating project {}/{}", owner, repo);

View File

@@ -24,6 +24,7 @@ import org.jreleaser.model.releaser.spi.Repository;
import org.jreleaser.sdk.git.GitSdk;
import org.jreleaser.sdk.gitlab.api.FileUpload;
import org.jreleaser.sdk.gitlab.api.GitlabAPIException;
import org.jreleaser.sdk.gitlab.api.Milestone;
import org.jreleaser.sdk.gitlab.api.Project;
import org.jreleaser.sdk.gitlab.api.Release;
@@ -32,6 +33,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author Andres Almiray
@@ -134,7 +136,7 @@ public class GitlabReleaser implements Releaser {
}
// local tag
if (deleteTags || !context.getModel().getRelease().getGitService().isSkipTagging()) {
if (deleteTags || !gitlab.isSkipTagging()) {
context.getLogger().debug("Tagging local repository with {}", tagName);
GitSdk.of(context).tag(tagName, true);
}
@@ -142,7 +144,7 @@ public class GitlabReleaser implements Releaser {
List<FileUpload> uploads = api.uploadAssets(gitlab.getOwner(), gitlab.getName(), assets);
Release release = new Release();
release.setName(gitlab.getResolvedReleaseName(context.getModel().getProject()));
release.setName(gitlab.getEffectiveReleaseName());
release.setTagName(gitlab.getEffectiveTagName(context.getModel().getProject()));
release.setRef(gitlab.getRef());
release.setDescription(changelog);
@@ -150,6 +152,16 @@ public class GitlabReleaser implements Releaser {
// remote tag/release
api.createRelease(gitlab.getOwner(), gitlab.getName(), release);
api.linkAssets(gitlab.getOwner(), gitlab.getName(), release, uploads);
Optional<Milestone> milestone = api.findMilestoneByName(
gitlab.getOwner(),
gitlab.getName(),
gitlab.getMilestone().getEffectiveName());
if (milestone.isPresent()) {
api.closeMilestone(gitlab.getOwner(),
gitlab.getName(),
milestone.get());
}
}
private void deleteTags(Gitlab api, String owner, String repo, String tagName) {

View File

@@ -61,4 +61,11 @@ public interface GitlabAPI {
@RequestLine("POST /projects/{projectId}/releases/{tagName}/assets/links")
@Headers("Content-Type: multipart/form-data")
Link linkAsset(LinkRequest link, @Param("projectId") Integer projectId, @Param("tagName") String tagName);
@RequestLine("GET /projects/{projectId}/milestones")
List<Milestone> findMilestoneByTitle(@Param("projectId") Integer projectId, @QueryMap Map<String, Object> queryMap);
@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);
}

View File

@@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2020-2021 Andres Almiray.
*
* 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.gitlab.api;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Andres Almiray
* @since 0.1.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Milestone {
private Integer id;
private Integer iid;
private Integer projectId;
private String title;
private String description;
private String state;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getIid() {
return iid;
}
public void setIid(Integer iid) {
this.iid = iid;
}
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}