From a817234a640a87c90aa600a7dcdb98ed124d8fbc Mon Sep 17 00:00:00 2001 From: Andres Almiray Date: Mon, 5 Apr 2021 19:10:16 +0200 Subject: [PATCH] [release] add support for Gitea. Fixes #10 --- .../jreleaser/engine/release/Releasers.java | 4 + .../main/java/org/jreleaser/model/Gitea.java | 1 - .../model/validation/GiteaValidator.java | 4 + .../jreleaser-workflow.gradle | 1 + gradle.properties | 1 + .../jreleaser/sdk/git/ChangelogGenerator.java | 20 +- sdks/gitea-java-sdk/gitea-java-sdk.gradle | 34 +++ sdks/gitea-java-sdk/gradle.properties | 19 ++ .../java/org/jreleaser/sdk/gitea/Gitea.java | 196 ++++++++++++++++++ .../jreleaser/sdk/gitea/GiteaException.java | 36 ++++ .../jreleaser/sdk/gitea/GiteaReleaser.java | 150 ++++++++++++++ .../sdk/gitea/GiteaReleaserBuilder.java | 33 +++ .../gitea/GiteaReleaserBuilderFactory.java | 38 ++++ .../org/jreleaser/sdk/gitea/api/GiteaAPI.java | 62 ++++++ .../sdk/gitea/api/GiteaAPIException.java | 62 ++++++ .../jreleaser/sdk/gitea/api/GtAttachment.java | 73 +++++++ .../sdk/gitea/api/GtOrganization.java | 73 +++++++ .../jreleaser/sdk/gitea/api/GtRelease.java | 100 +++++++++ .../jreleaser/sdk/gitea/api/GtRepository.java | 75 +++++++ sdks/gitlab-java-sdk/gitlab-java-sdk.gradle | 2 +- .../java/org/jreleaser/sdk/gitlab/Gitlab.java | 10 +- .../jreleaser/sdk/gitlab/GitlabReleaser.java | 1 - sdks/slack-java-sdk/slack-java-sdk.gradle | 2 +- sdks/zulip-java-sdk/zulip-java-sdk.gradle | 2 +- 24 files changed, 976 insertions(+), 23 deletions(-) create mode 100644 sdks/gitea-java-sdk/gitea-java-sdk.gradle create mode 100644 sdks/gitea-java-sdk/gradle.properties create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/Gitea.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaException.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaser.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilder.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilderFactory.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPI.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPIException.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtAttachment.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtOrganization.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRelease.java create mode 100644 sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRepository.java diff --git a/core/jreleaser-engine/src/main/java/org/jreleaser/engine/release/Releasers.java b/core/jreleaser-engine/src/main/java/org/jreleaser/engine/release/Releasers.java index a98c1c4d..b8cf90d3 100644 --- a/core/jreleaser-engine/src/main/java/org/jreleaser/engine/release/Releasers.java +++ b/core/jreleaser-engine/src/main/java/org/jreleaser/engine/release/Releasers.java @@ -17,6 +17,7 @@ */ package org.jreleaser.engine.release; +import org.jreleaser.model.Gitea; import org.jreleaser.model.Github; import org.jreleaser.model.Gitlab; import org.jreleaser.model.JReleaserContext; @@ -57,6 +58,9 @@ public class Releasers { if (null != context.getModel().getRelease().getGitlab()) { return (RB) builders.get(Gitlab.NAME); } + if (null != context.getModel().getRelease().getGitea()) { + return (RB) builders.get(Gitea.NAME); + } throw new JReleaserException("No suitable git releaser has been configured"); } diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/Gitea.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/Gitea.java index 4ecc6dc7..c4c41b0b 100644 --- a/core/jreleaser-model/src/main/java/org/jreleaser/model/Gitea.java +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/Gitea.java @@ -32,7 +32,6 @@ public class Gitea extends GitService { public Gitea() { super(NAME); - setHost("try.gitea.io"); setRepoUrlFormat("https://{{repoHost}}/{{repoOwner}}/{{repoName}}"); setCommitUrlFormat("https://{{repoHost}}/{{repoOwner}}/{{repoName}}/commits"); setDownloadUrlFormat("https://{{repoHost}}/{{repoOwner}}/{{repoName}}/releases/download/{{tagName}}/{{artifactFileName}}"); diff --git a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/GiteaValidator.java b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/GiteaValidator.java index 8125549b..083ecb77 100644 --- a/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/GiteaValidator.java +++ b/core/jreleaser-model/src/main/java/org/jreleaser/model/validation/GiteaValidator.java @@ -34,6 +34,10 @@ public abstract class GiteaValidator extends GitServiceValidator { validateGitService(context, gitea, errors); + if (isBlank(gitea.getApiEndpoint())) { + errors.add("gitea.apiEndpoint must not be blank"); + } + if (isBlank(gitea.getTargetCommitish())) { gitea.setTargetCommitish("main"); } diff --git a/core/jreleaser-workflow/jreleaser-workflow.gradle b/core/jreleaser-workflow/jreleaser-workflow.gradle index 95054183..4a503b5f 100644 --- a/core/jreleaser-workflow/jreleaser-workflow.gradle +++ b/core/jreleaser-workflow/jreleaser-workflow.gradle @@ -26,6 +26,7 @@ dependencies { // release api project(':github-java-sdk') api project(':gitlab-java-sdk') + api project(':gitea-java-sdk') // tools api project(':jreleaser-tools') } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ff1eb222..964bfc37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -30,6 +30,7 @@ asmVersion = 8.0.1 awaitilityVersion = 4.0.3 bouncyCastleVersion = 1.68 feignVersion = 10.12 +feignFormVersion = 3.8.0 githubVersion = 1.125 greenmailVersion = 1.6.3 guavaVersion = 30.1-jre diff --git a/sdks/git-sdk/src/main/java/org/jreleaser/sdk/git/ChangelogGenerator.java b/sdks/git-sdk/src/main/java/org/jreleaser/sdk/git/ChangelogGenerator.java index d778c7b2..6655c5b7 100644 --- a/sdks/git-sdk/src/main/java/org/jreleaser/sdk/git/ChangelogGenerator.java +++ b/sdks/git-sdk/src/main/java/org/jreleaser/sdk/git/ChangelogGenerator.java @@ -25,7 +25,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; import org.jreleaser.model.Changelog; import org.jreleaser.model.GitService; -import org.jreleaser.model.Github; +import org.jreleaser.model.Gitlab; import org.jreleaser.model.JReleaserContext; import java.io.IOException; @@ -37,7 +37,6 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; import static org.jreleaser.sdk.git.GitSdk.extractTagName; -import static org.jreleaser.util.StringUtils.isNotBlank; /** * @author Andres Almiray @@ -58,7 +57,7 @@ public class ChangelogGenerator { String commitsUrl = gitService.getResolvedCommitUrl(context.getModel().getProject()); String separator = System.lineSeparator(); - if (!Github.NAME.equals(gitService.getServiceName())) { + if (Gitlab.NAME.equals(gitService.getServiceName())) { separator += System.lineSeparator(); } String commitSeparator = separator; @@ -92,16 +91,11 @@ public class ChangelogGenerator { String[] input = commit.getFullMessage().trim().split(System.lineSeparator()); List lines = new ArrayList<>(); - for (int i = 0; i < input.length; i++) { - if (i == 0) { - if (changelog.isLinks()) { - lines.add("[" + abbreviation + "](" + commitsUrl + "/" + commitHash + ") " + input[i].trim()); - } else { - lines.add(abbreviation + " " + input[i].trim()); - } - } else if (isNotBlank(input[i])) { - lines.add(" " + input[i].trim()); - } + + if (changelog.isLinks()) { + lines.add("[" + abbreviation + "](" + commitsUrl + "/" + commitHash + ") " + input[0].trim()); + } else { + lines.add(abbreviation + " " + input[0].trim()); } return String.join(commitSeparator, lines); diff --git a/sdks/gitea-java-sdk/gitea-java-sdk.gradle b/sdks/gitea-java-sdk/gitea-java-sdk.gradle new file mode 100644 index 00000000..9c5301c4 --- /dev/null +++ b/sdks/gitea-java-sdk/gitea-java-sdk.gradle @@ -0,0 +1,34 @@ +/* + * 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. + */ +dependencies { + compileOnly "org.kordamp.jipsy:jipsy-annotations:${jipsyVersion}" + annotationProcessor "org.kordamp.jipsy:jipsy-processor:${jipsyVersion}" + + api project(':jreleaser-model') + api project(':git-sdk') + + api "org.apache.tika:tika-core:$tikaVersion" + + api "io.github.openfeign:feign-core:$feignVersion" + api "io.github.openfeign:feign-jackson:$feignVersion" + api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion" + api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" + api "io.github.openfeign.form:feign-form:$feignFormVersion" + + testImplementation "com.github.tomakehurst:wiremock-jre8:$wiremockVersion" +} \ No newline at end of file diff --git a/sdks/gitea-java-sdk/gradle.properties b/sdks/gitea-java-sdk/gradle.properties new file mode 100644 index 00000000..fd8bf4c2 --- /dev/null +++ b/sdks/gitea-java-sdk/gradle.properties @@ -0,0 +1,19 @@ +# +# 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. +# + +project_description = Java SDK for Gitea diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/Gitea.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/Gitea.java new file mode 100644 index 00000000..d3e002cb --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/Gitea.java @@ -0,0 +1,196 @@ +/* + * 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; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.SerializationFeature; +import feign.Feign; +import feign.Request; +import feign.form.FormData; +import feign.form.FormEncoder; +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.GtOrganization; +import org.jreleaser.sdk.gitea.api.GtRelease; +import org.jreleaser.sdk.gitea.api.GtRepository; +import org.jreleaser.util.CollectionUtils; +import org.jreleaser.util.JReleaserLogger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +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; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +class Gitea { + private static final String API_V1 = "/api/v1"; + private final Tika tika = new Tika(); + + private final JReleaserLogger logger; + private final GiteaAPI api; + + Gitea(JReleaserLogger logger, String endpoint, String token) throws IOException { + requireNonNull(logger, "'logger' must not be blank"); + requireNonBlank(token, "'token' must not be blank"); + requireNonBlank(endpoint, "'endpoint' must not be blank"); + + if (!endpoint.endsWith(API_V1)) { + if (endpoint.endsWith("/")) { + endpoint = endpoint.substring(0, endpoint.length() - 1); + } + endpoint += API_V1; + } + + ObjectMapper objectMapper = new ObjectMapper() + .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(SerializationFeature.INDENT_OUTPUT, true); + + this.logger = logger; + this.api = Feign.builder() + .encoder(new FormEncoder(new JacksonEncoder(objectMapper))) + .decoder(new JacksonDecoder(objectMapper)) + .requestInterceptor(template -> template.header("Authorization", String.format("token %s", token))) + .errorDecoder((methodKey, response) -> new GiteaAPIException(response.status(), response.reason(), response.headers())) + .options(new Request.Options(20, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true)) + .target(GiteaAPI.class, endpoint); + } + + GtRepository findRepository(String owner, String repo) { + logger.debug("Lookup repository {}/{}", owner, repo); + try { + return api.getRepository(owner, repo); + } catch (GiteaAPIException e) { + if (e.isNotFound()) { + // ok + return null; + } + throw e; + } + } + + GtRepository createRepository(String owner, String repo) { + logger.debug("Creating repository {}/{}", owner, repo); + + Map params = CollectionUtils.map() + .e("name", repo) + .e("private", false); + + GtOrganization organization = resolveOrganization(owner); + if (null != organization) { + return api.createRepository(params, owner); + } + + return api.createRepository(params); + } + + private GtOrganization resolveOrganization(String name) { + try { + return api.getOrganization(name); + } catch (GiteaAPIException e) { + if (e.isNotFound()) { + // ok + return null; + } + throw e; + } + } + + GtRelease findReleaseByTag(String owner, String repo, String tagName) { + logger.debug("Fetching release on {}/{} with tag {}", owner, repo, tagName); + + try { + return api.getReleaseByTagName(owner, repo, tagName); + } catch (GiteaAPIException e) { + if (e.isNotFound()) { + // ok + return null; + } + throw e; + } + } + + void deleteRelease(String owner, String repo, String tagName, Integer id) throws GiteaAPIException { + logger.debug("Deleting release {} from {}/{} ({})", tagName, owner, repo, id); + + try { + api.deleteRelease(owner, repo, id); + } catch (GiteaAPIException e) { + if (e.isNotFound()) { + // OK. Release might have been deleted but + // tag still exists. + return; + } + throw e; + } + } + + void deleteTag(String owner, String repo, String tagName) throws GiteaAPIException { + logger.debug("Deleting tag {} from {}/{}", tagName, owner, repo); + + api.deleteTag(owner, repo, tagName); + } + + GtRelease createRelease(String owner, String repo, GtRelease release) throws GiteaAPIException { + logger.debug("Creating release on {}/{} with tag {}", owner, repo, release.getTagName()); + + return api.createRelease(release, owner, repo); + } + + void uploadAssets(String owner, String repo, GtRelease release, List assets) throws IOException { + for (Path asset : assets) { + if (0 == asset.toFile().length() || !Files.exists(asset)) { + // do not upload empty or non existent files + continue; + } + + logger.info(" - Uploading {}", asset.getFileName().toString()); + try { + api.uploadAsset(owner, repo, release.getId(), toFormData(asset)); + } catch (GiteaAPIException e) { + logger.error(" x Failed to upload {}", asset.getFileName()); + throw e; + } + } + } + + private FormData toFormData(Path asset) throws IOException { + return FormData.builder() + .fileName(asset.getFileName().toString()) + .contentType(MediaType.parse(tika.detect(asset)).toString()) + .data(Files.readAllBytes(asset)) + .build(); + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaException.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaException.java new file mode 100644 index 00000000..dc00f268 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaException.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +public class GiteaException extends RuntimeException { + public GiteaException(String message) { + super(message); + } + + public GiteaException(String message, Throwable cause) { + super(message, cause); + } + + public GiteaException(Throwable cause) { + super(cause); + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaser.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaser.java new file mode 100644 index 00000000..74113970 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaser.java @@ -0,0 +1,150 @@ +/* + * 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; + +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.sdk.git.GitSdk; +import org.jreleaser.sdk.gitea.api.GiteaAPIException; +import org.jreleaser.sdk.gitea.api.GtRelease; +import org.jreleaser.sdk.gitea.api.GtRepository; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +public class GiteaReleaser implements Releaser { + private final JReleaserContext context; + private final List assets = new ArrayList<>(); + + GiteaReleaser(JReleaserContext context, List assets) { + this.context = context; + this.assets.addAll(assets); + } + + public void release() throws ReleaseException { + org.jreleaser.model.Gitea gitea = context.getModel().getRelease().getGitea(); + context.getLogger().info("Releasing to {}", gitea.getResolvedRepoUrl(context.getModel().getProject())); + String tagName = gitea.getEffectiveTagName(context.getModel().getProject()); + + try { + String changelog = context.getChangelog(); + + Gitea api = new Gitea(context.getLogger(), gitea.getApiEndpoint(), gitea.getResolvedToken()); + + context.getLogger().debug("Looking up release with tag {} at repository {}", tagName, gitea.getCanonicalRepoName()); + GtRelease release = api.findReleaseByTag(gitea.getOwner(), gitea.getName(), tagName); + if (null != release) { + context.getLogger().debug("Release {} exists", tagName); + if (gitea.isOverwrite()) { + context.getLogger().debug("Deleting release {}", tagName); + if (!context.isDryrun()) { + api.deleteRelease(gitea.getOwner(), gitea.getName(), tagName, release.getId()); + } + context.getLogger().debug("Creating release {}", tagName); + createRelease(api, tagName, changelog, context.getModel().getProject().isSnapshot()); + } else if (gitea.isAllowUploadToExisting()) { + context.getLogger().debug("Updating release {}", tagName); + if (!context.isDryrun()) { + api.uploadAssets(gitea.getOwner(), gitea.getName(), release, assets); + } + } else { + throw new IllegalStateException("Gitea release failed because release " + + tagName + " already exists. overwrite = false; allowUploadToExisting = false"); + } + } else { + context.getLogger().debug("Release {} does not exist", tagName); + context.getLogger().debug("Creating release {}", tagName); + createRelease(api, tagName, changelog, context.getModel().getProject().isSnapshot()); + } + } catch (IOException | IllegalStateException e) { + throw new ReleaseException(e); + } + } + + @Override + public Repository maybeCreateRepository(String owner, String repo, String password) throws IOException { + org.jreleaser.model.Gitea gitea = context.getModel().getRelease().getGitea(); + context.getLogger().debug("Looking up {}/{}", owner, repo); + + Gitea api = new Gitea(context.getLogger(), gitea.getApiEndpoint(), password); + GtRepository repository = api.findRepository(owner, repo); + if (null == repository) { + repository = api.createRepository(owner, repo); + } + + return new Repository( + owner, + repo, + repository.getHtmlUrl(), + repository.getCloneUrl()); + } + + private void createRelease(Gitea api, String tagName, String changelog, boolean deleteTags) throws IOException { + org.jreleaser.model.Gitea gitea = context.getModel().getRelease().getGitea(); + + if (context.isDryrun()) { + for (Path asset : assets) { + if (0 == asset.toFile().length() || !Files.exists(asset)) { + // do not upload empty or non existent files + continue; + } + + context.getLogger().debug(" - Uploading asset {}", asset.getFileName().toString()); + } + return; + } + + if (deleteTags) { + deleteTags(api, gitea.getOwner(), gitea.getName(), tagName); + } + + // local tag + if (deleteTags || !context.getModel().getRelease().getGitService().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.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); + } + + private void deleteTags(Gitea api, String owner, String repo, String tagName) { + // delete remote tag + try { + api.deleteTag(owner, repo, tagName); + } catch (GiteaAPIException ignored) { + //noop + } + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilder.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilder.java new file mode 100644 index 00000000..16e0e02f --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilder.java @@ -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.sdk.gitea; + +import org.jreleaser.model.releaser.spi.AbstractReleaserBuilder; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +public class GiteaReleaserBuilder extends AbstractReleaserBuilder { + @Override + public GiteaReleaser build() { + validate(); + + return new GiteaReleaser(context, assets); + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilderFactory.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilderFactory.java new file mode 100644 index 00000000..fe9a0018 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/GiteaReleaserBuilderFactory.java @@ -0,0 +1,38 @@ +/* + * 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; + +import org.jreleaser.model.releaser.spi.ReleaserBuilderFactory; +import org.kordamp.jipsy.annotations.ServiceProviderFor; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +@ServiceProviderFor(ReleaserBuilderFactory.class) +public class GiteaReleaserBuilderFactory implements ReleaserBuilderFactory { + @Override + public String getName() { + return org.jreleaser.model.Gitea.NAME; + } + + @Override + public GiteaReleaserBuilder getBuilder() { + return new GiteaReleaserBuilder(); + } +} \ No newline at end of file diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPI.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPI.java new file mode 100644 index 00000000..996a9c07 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPI.java @@ -0,0 +1,62 @@ +/* + * 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 feign.Headers; +import feign.Param; +import feign.RequestLine; +import feign.form.FormData; + +import java.util.Map; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +public interface GiteaAPI { + @RequestLine("GET /repos/{owner}/{repo}") + GtRepository getRepository(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("GET /orgs/{org}") + GtOrganization getOrganization(@Param("org") String org); + + @RequestLine("POST /orgs/{org}/repos") + @Headers("Content-Type: application/json") + GtRepository createRepository(Map data, @Param("org") String org); + + @RequestLine("POST /user/repos") + @Headers("Content-Type: application/json") + GtRepository createRepository(Map data); + + @RequestLine("GET /repos/{owner}/{repo}/releases/tags/{tag}") + GtRelease getReleaseByTagName(@Param("owner") String owner, @Param("repo") String repo, @Param("tag") String tag); + + @RequestLine("DELETE /repos/{owner}/{repo}/releases/{id}") + void deleteRelease(@Param("owner") String owner, @Param("repo") String repo, @Param("id") Integer id); + + @RequestLine("DELETE /repos/{owner}/{repo}/releases/tags/{tag}") + void deleteTag(@Param("owner") String owner, @Param("repo") String repo, @Param("tag") String tag); + + @RequestLine("POST /repos/{owner}/{repo}/releases") + @Headers("Content-Type: application/json") + GtRelease createRelease(GtRelease release, @Param("owner") String owner, @Param("repo") String repo); + + @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); +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPIException.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPIException.java new file mode 100644 index 00000000..834ac1ae --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GiteaAPIException.java @@ -0,0 +1,62 @@ +/* + * 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 java.util.Collection; +import java.util.Collections; +import java.util.Map; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +public class GiteaAPIException extends RuntimeException { + private final int status; + private final String reason; + private final Map> headers; + + public GiteaAPIException(int status, String reason) { + this(status, reason, Collections.emptyMap()); + } + + public GiteaAPIException(int status, String reason, Map> headers) { + this.status = status; + this.reason = reason; + this.headers = headers; + } + + public int getStatus() { + return status; + } + + public String getReason() { + return reason; + } + + public Map> getHeaders() { + return headers; + } + + public boolean isNotFound() { + return 404 == status; + } + + public boolean isForbidden() { + return 403 == status; + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtAttachment.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtAttachment.java new file mode 100644 index 00000000..e3c89766 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtAttachment.java @@ -0,0 +1,73 @@ +/* + * 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 GtAttachment { + private String browserDownloadUrl; + private Integer id; + private String name; + private Integer size; + private String uuid; + + public String getBrowserDownloadUrl() { + return browserDownloadUrl; + } + + public void setBrowserDownloadUrl(String browserDownloadUrl) { + this.browserDownloadUrl = browserDownloadUrl; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtOrganization.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtOrganization.java new file mode 100644 index 00000000..c1a8a584 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtOrganization.java @@ -0,0 +1,73 @@ +/* + * 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 GtOrganization { + private Integer id; + private String username; + private String fullName; + private String description; + private String website; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getWebsite() { + return website; + } + + public void setWebsite(String website) { + this.website = website; + } +} \ No newline at end of file diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRelease.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRelease.java new file mode 100644 index 00000000..8df3db33 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRelease.java @@ -0,0 +1,100 @@ +/* + * 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 GtRelease { + private Integer id; + private String name; + private String htmlUrl; + private String tagName; + private String url; + private String body; + private boolean prerelease; + private String targetCommitish; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public void setHtmlUrl(String htmlUrl) { + this.htmlUrl = htmlUrl; + } + + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public boolean isPrerelease() { + return prerelease; + } + + public void setPrerelease(boolean prerelease) { + this.prerelease = prerelease; + } + + public String getTargetCommitish() { + return targetCommitish; + } + + public void setTargetCommitish(String targetCommitish) { + this.targetCommitish = targetCommitish; + } +} \ No newline at end of file diff --git a/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRepository.java b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRepository.java new file mode 100644 index 00000000..6c7477b4 --- /dev/null +++ b/sdks/gitea-java-sdk/src/main/java/org/jreleaser/sdk/gitea/api/GtRepository.java @@ -0,0 +1,75 @@ +/* + * 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; + +import java.util.Date; + +/** + * @author Andres Almiray + * @since 0.1.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GtRepository { + private Integer id; + private String name; + private String fullName; + private String htmlUrl; + private String cloneUrl; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public void setHtmlUrl(String htmlUrl) { + this.htmlUrl = htmlUrl; + } + + public String getCloneUrl() { + return cloneUrl; + } + + public void setCloneUrl(String cloneUrl) { + this.cloneUrl = cloneUrl; + } +} diff --git a/sdks/gitlab-java-sdk/gitlab-java-sdk.gradle b/sdks/gitlab-java-sdk/gitlab-java-sdk.gradle index 9fc49a10..9c5301c4 100644 --- a/sdks/gitlab-java-sdk/gitlab-java-sdk.gradle +++ b/sdks/gitlab-java-sdk/gitlab-java-sdk.gradle @@ -28,7 +28,7 @@ dependencies { api "io.github.openfeign:feign-jackson:$feignVersion" api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion" api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - api 'io.github.openfeign.form:feign-form:3.8.0' + api "io.github.openfeign.form:feign-form:$feignFormVersion" testImplementation "com.github.tomakehurst:wiremock-jre8:$wiremockVersion" } \ No newline at end of file diff --git a/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/Gitlab.java b/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/Gitlab.java index 1774072a..769604a3 100644 --- a/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/Gitlab.java +++ b/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/Gitlab.java @@ -57,7 +57,7 @@ import static org.jreleaser.util.StringUtils.requireNonBlank; */ class Gitlab { static final String ENDPOINT = "https://gitlab.com/api/v4"; - private static final String API_V_4 = "/api/v4"; + private static final String API_V4 = "/api/v4"; private final Tika tika = new Tika(); private final JReleaserLogger logger; @@ -75,14 +75,14 @@ class Gitlab { endpoint = ENDPOINT; } - if (!endpoint.endsWith(API_V_4)) { + if (!endpoint.endsWith(API_V4)) { if (endpoint.endsWith("/")) { endpoint = endpoint.substring(0, endpoint.length() - 1); } - endpoint += API_V_4; + endpoint += API_V4; } - apiHost = endpoint.substring(0, endpoint.length() - API_V_4.length()); + apiHost = endpoint.substring(0, endpoint.length() - API_V4.length()); ObjectMapper objectMapper = new ObjectMapper() .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) @@ -233,7 +233,7 @@ class Gitlab { private FormData toFormData(Path asset) throws IOException { return FormData.builder() .fileName(asset.getFileName().toString()) - .contentType(MediaType.parse(new Tika().detect(asset)).toString()) + .contentType(MediaType.parse(tika.detect(asset)).toString()) .data(Files.readAllBytes(asset)) .build(); } diff --git a/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/GitlabReleaser.java b/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/GitlabReleaser.java index 4f1a6c68..13f59c94 100644 --- a/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/GitlabReleaser.java +++ b/sdks/gitlab-java-sdk/src/main/java/org/jreleaser/sdk/gitlab/GitlabReleaser.java @@ -139,7 +139,6 @@ public class GitlabReleaser implements Releaser { GitSdk.of(context).tag(tagName, true); } - List uploads = api.uploadAssets(gitlab.getOwner(), gitlab.getName(), assets); Release release = new Release(); diff --git a/sdks/slack-java-sdk/slack-java-sdk.gradle b/sdks/slack-java-sdk/slack-java-sdk.gradle index ff708cb6..210054f0 100644 --- a/sdks/slack-java-sdk/slack-java-sdk.gradle +++ b/sdks/slack-java-sdk/slack-java-sdk.gradle @@ -25,7 +25,7 @@ dependencies { api "io.github.openfeign:feign-jackson:$feignVersion" api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion" api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - api 'io.github.openfeign.form:feign-form:3.8.0' + api "io.github.openfeign.form:feign-form:$feignFormVersion" testImplementation "com.github.tomakehurst:wiremock-jre8:$wiremockVersion" } \ No newline at end of file diff --git a/sdks/zulip-java-sdk/zulip-java-sdk.gradle b/sdks/zulip-java-sdk/zulip-java-sdk.gradle index ff708cb6..210054f0 100644 --- a/sdks/zulip-java-sdk/zulip-java-sdk.gradle +++ b/sdks/zulip-java-sdk/zulip-java-sdk.gradle @@ -25,7 +25,7 @@ dependencies { api "io.github.openfeign:feign-jackson:$feignVersion" api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion" api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - api 'io.github.openfeign.form:feign-form:3.8.0' + api "io.github.openfeign.form:feign-form:$feignFormVersion" testImplementation "com.github.tomakehurst:wiremock-jre8:$wiremockVersion" } \ No newline at end of file