import com.google.gson.Gson import org.apache.commons.codec.Charsets import org.apache.http.HttpEntity import org.apache.http.HttpResponse import org.apache.http.client.HttpClient import org.apache.http.client.config.CookieSpecs import org.apache.http.client.config.RequestConfig import org.apache.http.client.methods.CloseableHttpResponse import org.apache.http.client.methods.HttpGet import org.apache.http.client.methods.HttpPost import org.apache.http.entity.ContentType import org.apache.http.entity.mime.MultipartEntityBuilder import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClientBuilder import org.apache.http.impl.client.HttpClients import java.util.regex.Matcher import java.util.regex.Pattern buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } mavenCentral() mavenLocal() jcenter() } dependencies { classpath "org.apache.httpcomponents:httpmime:4.5.13" classpath "com.google.code.gson:gson:2.8.9" classpath "org.apache.httpcomponents:httpclient:4.5.13" } } plugins { id 'net.researchgate.release' version '2.8.1' } apply plugin: 'java' apply plugin: 'idea' def branch = System.getenv("GITHUB_REF"); if (branch != null) { branch = branch.replace('refs/heads/', '') } def isCanary = version.toString().contains('canary') group = 'com.sekwah.advancedportals' description = "" sourceCompatibility = 1.8 targetCompatibility = 1.8 tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } String getPluginData(String tag) { File file = file("src/main/resources/plugin.yml") String version = "notfound" file.readLines("UTF-8").each { String line -> line = line.trim() if (line.startsWith(tag)) { version = line.substring(tag.length() + 2, line.length()) } } println "Advanced Portals v" + version return version } configurations { // configuration that holds jars to copy into lib includeLibs } repositories { maven { url "https://repo.maven.apache.org/maven2" } maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://nexus.velocitypowered.com/repository/maven-public/" } maven { url 'https://papermc.io/repo/repository/maven-public/' } maven { url 'https://maven.enginehub.org/repo/' } // WorldEdit } // includeLibs just says to include the library in the final jar dependencies { //implementation "org.bukkit:bukkit:1.16.1-R0.1-SNAPSHOT" implementation "org.spigotmc:spigot-api:1.16.1-R0.1-SNAPSHOT" implementation "net.md-5:bungeecord-api:1.16-R0.4" implementation "com.velocitypowered:velocity-api:3.1.0" annotationProcessor "com.velocitypowered:velocity-api:3.1.0" implementation "io.netty:netty-all:4.1.71.Final" compileOnly 'com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT' implementation "com.sk89q.worldedit:worldedit-bukkit:7.2.6" //compile fileTree(dir: 'libs', include: ['*.jar']) } /** For pre-releases and testers to be able to try the latest commits if they want. * If the builds start exceeding 8MB then we may want to upload to s3 instead and periodically clear. * TODO possibly add a task that announces when builds are made? * Though add a note that it may take a while for Curse to approve the files. */ task discordupload { dependsOn(jar) doLast { String discordWebhook = System.getenv("DISCORD_WEBHOOK") if (discordWebhook != null) { println("Logging Into Discord") CloseableHttpClient httpClient = HttpClients.createDefault() HttpPost uploadFile = new HttpPost(discordWebhook) MultipartEntityBuilder builder = MultipartEntityBuilder.create() if (isCanary) { builder.addTextBody("content", "New canary Build") } else { builder.addTextBody("content", "New release build\n\n" + "Current Features: <${project.github}/blob/${branch}/CHANGELOG.md>") } builder.addBinaryBody("file", file(jar.archiveFile).newInputStream(), ContentType.APPLICATION_OCTET_STREAM, jar.archiveName) HttpEntity multipart = builder.build() uploadFile.setEntity(multipart) CloseableHttpResponse response = httpClient.execute(uploadFile) response.getEntity() println("Posted build") } else { println("Discord webhook unspecified") } } } String getValueFromCurseAPI(apiKey, endpoint) { String API_BASE_URL = 'https://minecraft.curseforge.com' Gson gson = new Gson() HttpClient client = HttpClientBuilder.create() .setDefaultRequestConfig(RequestConfig.custom() .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build()).build() HttpGet get = new HttpGet(API_BASE_URL + endpoint) get.setHeader('X-Api-Token', apiKey) HttpResponse response = client.execute(get) int statusCode = response.statusLine.statusCode if (statusCode == 200) { byte[] data = response.entity.content.bytes return new String(data, Charsets.UTF_8) } else { if (response.getFirstHeader('content-type').value.contains('json')) { InputStreamReader reader = new InputStreamReader(response.entity.content) reader.close() throw new RuntimeException("[CurseForge] Error") } else { throw new RuntimeException("[CurseForge] HTTP Error Code $response.statusLine.statusCode: $response.statusLine.reasonPhrase") } } return "" } /** * Upload a single file (in case you also want to upload the other files like source n stuff) * @param json * @param file * @return * @throws IOException * @throws URISyntaxException */ UploadResponse uploadFile(Metadata metadata, File file, String apiKey, Gson gson) throws IOException, URISyntaxException { String API_BASE_URL = 'https://minecraft.curseforge.com' String UPLOAD_URL = "/api/projects/%s/upload-file" // Upload // Important info String uploadUrl = String.format(API_BASE_URL + UPLOAD_URL, project.curse_project_id) HttpClient client = HttpClientBuilder.create() .setDefaultRequestConfig(RequestConfig.custom() .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build()).build() HttpPost post = new HttpPost(uploadUrl) post.setHeader('X-Api-Token', apiKey) // https://support.curseforge.com/en/support/solutions/articles/9000197321-curseforge-api post.setEntity(MultipartEntityBuilder.create() .addTextBody('metadata', gson.toJson(metadata), ContentType.APPLICATION_JSON) .addBinaryBody('file', file) .build()) HttpResponse response = client.execute(post) InputStreamReader reader = new InputStreamReader(response.entity.content) UploadResponse uploadResponse = gson.fromJson(reader, UploadResponse) reader.close() return uploadResponse } class GameVersion { int id int gameVersionTypeID String name String slug } /** * As described here https://support.curseforge.com/en/support/solutions/articles/9000197321-curseforge-api */ class Metadata { String changelog String changelogType int[] gameVersions String releaseType } class UploadResponse { int id; } // Based on https://github.com/matthewprenger/CurseGradle as it didnt support Bukkit uploads at the time. task curseforge { dependsOn(jar) doLast { String apiKey = null if (System.getenv("CURSE_API") != null) { apiKey = System.getenv("CURSE_API") } if (apiKey != null) { Gson gson = new Gson() //String VERSION_TYPES_URL = "/api/game/version-types" int gameVersionTypeID = 1 String VERSION_URL = "/api/game/versions" println("Uploading to CurseForge") // Get game versions String gameVersionsString = getValueFromCurseAPI(apiKey, VERSION_URL) GameVersion[] gameVersions = gson.fromJson(gameVersionsString, GameVersion[].class) def versions = gameVersions.findAll { it.gameVersionTypeID == gameVersionTypeID } String[] supportedVersions = [ "1.18", "1.17", "1.16", "1.15", "1.14", "1.13" ] def supportedGameVersions = versions.findAll { supportedVersions.contains(it.name) } int[] supportedGameVersionIds = supportedGameVersions.collect { it.id }.toArray() println("Supported Version Id's ${supportedGameVersionIds}") Metadata uploadMetadata = new Metadata(); uploadMetadata.changelog = "${project.github}/blob/${branch}/CHANGELOG.md" uploadMetadata.changelogType = "markdown" uploadMetadata.releaseType = "release" uploadMetadata.gameVersions = supportedGameVersionIds def uploadId = uploadFile(uploadMetadata, file(jar.archiveFile), apiKey, gson) println("Uploaded with ID: ${uploadId.id}") println("Published build") } else { println("Discord webhook unspecified") } } // id = project.curse_project_id // // TODO add code to reference this but also cut the latest change logs in for the files // changelogType = 'markdown' // releaseType = 'release' } task copyPlugin { doLast { copy { if (System.env.MC_SERVER_LOC == null) { throw new Exception('You must set the server location and jar to use') } println "$buildDir/libs/Advanced-Portals-${version}.jar" println "${System.env.MC_SERVER_LOC}/plugins/Advanced-Portals-${version}.jar" try { delete fileTree("${System.env.MC_SERVER_LOC}/plugins/") { include "*.jar" } } catch (RuntimeException e) { println e.getLocalizedMessage() } from file("$buildDir/libs/Advanced-Portals-${version}.jar") into file("${System.env.MC_SERVER_LOC}/plugins/") } } } // Set SPIGOT_LOC to the location of your server and SPIGOT_JAR as the name of the jar file in the server you want to run // DIReallyKnowWhatIAmDoingISwear is to remove the stupid pause spigot has at the start task runJar() { doLast { if (System.env.MC_SERVER_LOC == null || System.env.MC_SERVER_JAR == null) { throw new Exception('You must set the server location and jar to use MC_SERVER_LOC and MC_SERVER_JAR') } javaexec { main "-jar" args "${System.env.MC_SERVER_LOC}\\${System.env.MC_SERVER_JAR}.jar" jvmArgs = ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005", "-DIReallyKnowWhatIAmDoingISwear=true"] workingDir "${System.env.MC_SERVER_LOC}" } } } task publish { doLast { println "This is a dummy task to run others for version: ${version}" } } // https://github.com/researchgate/gradle-release // Only other plugin I can find using auto & gradle https://github.com/intuit/hooks release { failOnPublishNeeded = false failOnSnapshotDependencies = false git { requireBranch = '' } // Disable tasks because something we have is causing -x to be ignored createReleaseTag.enabled = false preTagCommit.enabled = false commitNewVersion.enabled = false } task cleanbuildfolder { doFirst { println "Cleaning up previous builds (to stop publishing old ones by mistake)" project.delete(files("${buildDir}/libs")) } } void updateFileVersion(String fileLoc, Pattern pattern, Closure stringClosure) { File file = new File(projectDir, fileLoc) Matcher m = pattern.matcher(file.text) if (m.find()) { def newVersion = stringClosure.call(m) println "Replacing ${pattern.toString()} in ${fileLoc} with '${newVersion}'" file.text = file.text.replaceAll(pattern, newVersion) } } // Just to keep all numbers the same (as they are dotted all over the place) task updateVersionNumbers { doFirst { def versionRegex = ~/(\nversion:\s)([0-9.-]+)/ def velocityVersionRegex = ~/(\sversion\s=\s")([0-9.-]+)("\))/ updateFileVersion("src/main/resources/bungee.yml", versionRegex, { Matcher m -> return "${m.group(1)}${getVersion()}" }) updateFileVersion("src/main/resources/plugin.yml", versionRegex, { Matcher m -> return "${m.group(1)}${getVersion()}" }) updateFileVersion("src/main/java/com/sekwah/advancedportals/velocity/AdvancedPortalsPlugin.java", velocityVersionRegex, { Matcher m -> return "${m.group(1)}${getVersion()}${m.group(3)}" }) } } updateVersion.finalizedBy 'updateVersionNumbers' compileJava.dependsOn 'cleanbuildfolder' // Publish rules // Current behavior seems to be canary or release. Though pre-releases may break this pattern. publish.dependsOn 'build' publish.finalizedBy 'discordupload' if (!isCanary) { publish.finalizedBy 'curseforge' }