govno kod
Some checks failed
Build / build (push) Failing after 3h3m20s
Some checks failed
Build / build (push) Failing after 3h3m20s
This commit is contained in:
2
release-scripts/hangar/.gitignore
vendored
Normal file
2
release-scripts/hangar/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
.metals
|
||||
257
release-scripts/hangar/HangarVersionUploader.java
Normal file
257
release-scripts/hangar/HangarVersionUploader.java
Normal file
@@ -0,0 +1,257 @@
|
||||
//| mvnDeps:
|
||||
//| - com.google.code.gson:gson:2.13.2
|
||||
//| - org.apache.httpcomponents.client5:httpclient5:5.6
|
||||
//| - org.slf4j:slf4j-api:2.0.17
|
||||
//| - org.slf4j:slf4j-jdk14:2.0.17
|
||||
//| - org.checkerframework:checker-qual:4.0.0
|
||||
//| - org.yaml:snakeyaml:2.6
|
||||
|
||||
// Adapted from: https://gist.github.com/kennytv/a227d82249f54e0ad35005330256fee2
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||
import org.apache.hc.client5.http.entity.mime.FileBody;
|
||||
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.hc.client5.http.entity.mime.StringBody;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.apache.hc.core5.http.ContentType;
|
||||
import org.apache.hc.core5.http.HttpMessage;
|
||||
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.io.InputStream;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class HangarVersionUploader {
|
||||
|
||||
private static final String HANGAR_API_PRODUCTION = "https://hangar.papermc.io/api/v1";
|
||||
private static final String HANGAR_API_STAGING = "https://hangar.papermc.io/api/v1";
|
||||
private static final String HANGAR_API_LOCAL = "http://localhost:3333/api/v1";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HangarVersionUploader.class);
|
||||
private static final String HANGAR_API_URL = HANGAR_API_PRODUCTION;
|
||||
private static final Gson GSON = new Gson();
|
||||
private final String apiKey;
|
||||
private ActiveJWT activeJWT;
|
||||
|
||||
public HangarVersionUploader(final String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
private static String readPluginVersion(Path pluginFile) {
|
||||
try (ZipFile zipFile = new ZipFile(pluginFile.toFile())) {
|
||||
|
||||
ZipEntry entry = zipFile.getEntry("plugin.yml");
|
||||
|
||||
if (entry != null) {
|
||||
try (InputStream inputStream = zipFile.getInputStream(entry)) {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> data = yaml.load(inputStream);
|
||||
return (String) data.get("version");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
throw new RuntimeException("plugin.yml file inside plugin jar missing.");
|
||||
}
|
||||
|
||||
private static String getApiKey() {
|
||||
return System.getenv("HANGAR_TOKEN");
|
||||
}
|
||||
|
||||
private static String[] getMinecraftVersions() {
|
||||
return System.getenv("MINECRAFT_VERSIONS").split(" ");
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
final String projectName = "InvSee-plus-plus";
|
||||
final String apiKey = getApiKey();
|
||||
final List<Path> filePaths = List.of(
|
||||
Path.of("plugins/InvSee++.jar"),
|
||||
Path.of("plugins/InvSee++_Give.jar"),
|
||||
Path.of("plugins/InvSee++_Clear.jar")
|
||||
);
|
||||
|
||||
String pluginVersion = readPluginVersion(filePaths.get(0));
|
||||
List<String> minecraftVersions = List.of(getMinecraftVersions());
|
||||
String channel = pluginVersion.endsWith("-SNAPSHOT") ? "Alpha" : "Release";
|
||||
|
||||
final List<MultipartFileOrUrl> fileInfo = List.of(
|
||||
new MultipartFileOrUrl(List.of(Platform.PAPER), null)
|
||||
// TODO are these wrong? should they be here for InvSee++_Give and InvSee++_Clear? Hangar rejects them when I use these.
|
||||
// new MultipartFileOrUrl(List.of(Platform.PAPER), null),
|
||||
// new MultipartFileOrUrl(List.of(Platform.PAPER), null)
|
||||
);
|
||||
final VersionUpload versionUpload = new VersionUpload(
|
||||
pluginVersion,
|
||||
Map.of(
|
||||
Platform.PAPER, List.of()
|
||||
),
|
||||
Map.of(
|
||||
Platform.PAPER, minecraftVersions
|
||||
),
|
||||
"Automated deployment for InvSee++ v" + pluginVersion,
|
||||
fileInfo,
|
||||
channel
|
||||
);
|
||||
LOGGER.info("VersionUpload: {}.", versionUpload);
|
||||
|
||||
final HangarVersionUploader uploader = new HangarVersionUploader(apiKey);
|
||||
try (final CloseableHttpClient client = HttpClients.createDefault()) {
|
||||
uploader.uploadVersion(client, projectName, versionUpload, filePaths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a new version to Hangar.
|
||||
*
|
||||
* @param client http client to use
|
||||
* @param project unique project name
|
||||
* @param versionUpload version upload data
|
||||
* @param filePaths paths to the files to upload for platforms without external urls
|
||||
* @throws IOException if an error occurs while uploading
|
||||
*/
|
||||
public void uploadVersion(
|
||||
final HttpClient client,
|
||||
final String project,
|
||||
final VersionUpload versionUpload,
|
||||
final List<Path> filePaths
|
||||
) throws IOException {
|
||||
// The data needs to be sent as multipart form data
|
||||
final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.addPart("versionUpload", new StringBody(GSON.toJson(versionUpload), ContentType.APPLICATION_JSON));
|
||||
|
||||
// Attach files (one file for each platform where no external url is defined in the version upload data)
|
||||
for (final Path filePath : filePaths) {
|
||||
LOGGER.info("Adding file {} as multipart.", filePath.toAbsolutePath());
|
||||
builder.addPart("files", new FileBody(filePath.toFile(), ContentType.DEFAULT_BINARY));
|
||||
}
|
||||
|
||||
final HttpPost post = new HttpPost("%s/projects/%s/upload".formatted(HANGAR_API_URL, project));
|
||||
post.setEntity(builder.build());
|
||||
this.addAuthorizationHeader(client, post);
|
||||
|
||||
final boolean success = client.execute(post, response -> {
|
||||
if (response.getCode() != 200) {
|
||||
LOGGER.error("Error uploading version {}: {}", response.getCode(), response.getReasonPhrase());
|
||||
if (response instanceof org.apache.hc.core5.http.HttpEntityContainer entityResponse) {
|
||||
var httpEntity = response.getEntity();
|
||||
var contents = new String(httpEntity.getContent().readAllBytes(), StandardCharsets.UTF_8);
|
||||
LOGGER.error("Response body: {}", contents);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!success) {
|
||||
throw new RuntimeException("Error uploading version");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void addAuthorizationHeader(final HttpClient client, final HttpMessage message) throws IOException {
|
||||
if (this.activeJWT != null && !this.activeJWT.hasExpired()) {
|
||||
// Add the active JWT
|
||||
message.addHeader("Authorization", this.activeJWT.jwt());
|
||||
return;
|
||||
}
|
||||
|
||||
// Request a new JWT
|
||||
final ActiveJWT jwt = client.execute(new HttpPost("%s/authenticate?apiKey=%s".formatted(HANGAR_API_URL, this.apiKey)), response -> {
|
||||
if (response.getCode() == 400) {
|
||||
LOGGER.error("Bad JWT request; is the API key correct?");
|
||||
return null;
|
||||
} else if (response.getCode() != 200) {
|
||||
LOGGER.error("Error requesting JWT {}: {}", response.getCode(), response.getReasonPhrase());
|
||||
return null;
|
||||
}
|
||||
|
||||
final String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
final JsonObject object = GSON.fromJson(json, JsonObject.class);
|
||||
final String token = object.getAsJsonPrimitive("token").getAsString();
|
||||
final long expiresIn = object.getAsJsonPrimitive("expiresIn").getAsLong();
|
||||
return new ActiveJWT(token, System.currentTimeMillis() + expiresIn);
|
||||
});
|
||||
|
||||
if (jwt == null) {
|
||||
throw new RuntimeException("Error getting JWT");
|
||||
}
|
||||
|
||||
this.activeJWT = jwt;
|
||||
message.addHeader("Authorization", jwt.jwt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an active JSON Web Token used for authentication with Hangar.
|
||||
*
|
||||
* @param jwt Active JWT
|
||||
* @param expiresAt time in milliseconds when the JWT expires
|
||||
*/
|
||||
private record ActiveJWT(String jwt, long expiresAt) {
|
||||
|
||||
public boolean hasExpired() {
|
||||
// Make sure we request a new one before it expires
|
||||
return System.currentTimeMillis() < this.expiresAt + TimeUnit.SECONDS.toMillis(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record VersionUpload(String version, Map<Platform, List<PluginDependency>> pluginDependencies,
|
||||
Map<Platform, List<String>> platformDependencies, String description,
|
||||
List<MultipartFileOrUrl> files, String channel) {
|
||||
}
|
||||
|
||||
record PluginDependency(String name, boolean required, @Nullable String externalUrl) {
|
||||
|
||||
/**
|
||||
* Creates a new PluginDependency with the given name, whether the dependency is required, and the namespace of the dependency.
|
||||
*
|
||||
* @param hangarProjectName name of the dependency, being its Hangar project id
|
||||
* @param required whether the dependency is required
|
||||
* @return a new PluginDependency
|
||||
*/
|
||||
static PluginDependency createWithHangarNamespace(final String hangarProjectName, final boolean required) {
|
||||
return new PluginDependency(hangarProjectName, required, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PluginDependency with the given name, external url, and whether the dependency is required.
|
||||
*
|
||||
* @param name name of the dependency
|
||||
* @param required whether the dependency is required
|
||||
* @param externalUrl url to the dependency
|
||||
* @return a new PluginDependency
|
||||
*/
|
||||
static PluginDependency createWithUrl(final String name, final String externalUrl, final boolean required) {
|
||||
return new PluginDependency(name, required, externalUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum Platform {
|
||||
PAPER,
|
||||
WATERFALL,
|
||||
VELOCITY
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a file that is either uploaded or downloaded from an external url.
|
||||
*
|
||||
* @param platforms platforms the download is compatible with
|
||||
* @param externalUrl external url of the download, or null if the download is a file
|
||||
*/
|
||||
record MultipartFileOrUrl(List<Platform> platforms, @Nullable String externalUrl) {
|
||||
}
|
||||
199
release-scripts/mill-dist-1.1.5-mill.sh
Normal file
199
release-scripts/mill-dist-1.1.5-mill.sh
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "${DEFAULT_MILL_VERSION}" ] ; then DEFAULT_MILL_VERSION="1.1.5-jvm"; fi
|
||||
|
||||
if [ -z "${GITHUB_RELEASE_CDN}" ] ; then GITHUB_RELEASE_CDN=""; fi
|
||||
|
||||
if [ -z "$MILL_MAIN_CLI" ] ; then MILL_MAIN_CLI="${0}"; fi
|
||||
|
||||
MILL_REPO_URL="https://github.com/com-lihaoyi/mill"
|
||||
|
||||
MILL_BUILD_SCRIPT=""
|
||||
|
||||
if [ -f "build.mill" ] ; then
|
||||
MILL_BUILD_SCRIPT="build.mill"
|
||||
elif [ -f "build.mill.scala" ] ; then
|
||||
MILL_BUILD_SCRIPT="build.mill.scala"
|
||||
elif [ -f "build.sc" ] ; then
|
||||
MILL_BUILD_SCRIPT="build.sc"
|
||||
fi
|
||||
|
||||
# `s/.*://`:
|
||||
# This is a greedy match that removes everything from the beginning of the line up to (and including) the last
|
||||
# colon (:). This effectively isolates the value part of the declaration.
|
||||
#
|
||||
# `s/#.*//`:
|
||||
# This removes any comments at the end of the line.
|
||||
#
|
||||
# `s/['\"]//g`:
|
||||
# This removes all single and double quotes from the string, wherever they appear (g is for "global").
|
||||
#
|
||||
# `s/^[[:space:]]*//; s/[[:space:]]*$//`:
|
||||
# These two expressions trim any leading or trailing whitespace ([[:space:]] matches spaces and tabs).
|
||||
TRIM_VALUE_SED="s/.*://; s/#.*//; s/['\"]//g; s/^[[:space:]]*//; s/[[:space:]]*$//"
|
||||
|
||||
if [ -z "${MILL_VERSION}" ] ; then
|
||||
if [ -f ".mill-version" ] ; then
|
||||
MILL_VERSION="$(tr '\r' '\n' < .mill-version | head -n 1 2> /dev/null)"
|
||||
elif [ -f ".config/mill-version" ] ; then
|
||||
MILL_VERSION="$(tr '\r' '\n' < .config/mill-version | head -n 1 2> /dev/null)"
|
||||
elif [ -f "build.mill.yaml" ] ; then
|
||||
MILL_VERSION="$(grep -E "mill-version:" "build.mill.yaml" | sed -E "$TRIM_VALUE_SED")"
|
||||
elif [ -n "${MILL_BUILD_SCRIPT}" ] ; then
|
||||
MILL_VERSION="$(grep -E "//\|.*mill-version" "${MILL_BUILD_SCRIPT}" | sed -E "$TRIM_VALUE_SED")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${MILL_VERSION}" ] ; then MILL_VERSION="${DEFAULT_MILL_VERSION}"; fi
|
||||
|
||||
MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill"
|
||||
|
||||
if [ -z "${MILL_FINAL_DOWNLOAD_FOLDER}" ] ; then MILL_FINAL_DOWNLOAD_FOLDER="${MILL_USER_CACHE_DIR}/download"; fi
|
||||
|
||||
MILL_NATIVE_SUFFIX="-native"
|
||||
MILL_JVM_SUFFIX="-jvm"
|
||||
ARTIFACT_SUFFIX=""
|
||||
|
||||
# Check if GLIBC version is at least the required version
|
||||
# Returns 0 (true) if GLIBC >= required version, 1 (false) otherwise
|
||||
check_glibc_version() {
|
||||
required_version="2.39"
|
||||
required_major=$(echo "$required_version" | cut -d. -f1)
|
||||
required_minor=$(echo "$required_version" | cut -d. -f2)
|
||||
# Get GLIBC version from ldd --version (first line contains version like "ldd (GNU libc) 2.31")
|
||||
glibc_version=$(ldd --version 2>/dev/null | head -n 1 | grep -oE '[0-9]+\.[0-9]+$' || echo "")
|
||||
if [ -z "$glibc_version" ]; then
|
||||
# If we can't determine GLIBC version, assume it's too old
|
||||
return 1
|
||||
fi
|
||||
glibc_major=$(echo "$glibc_version" | cut -d. -f1)
|
||||
glibc_minor=$(echo "$glibc_version" | cut -d. -f2)
|
||||
if [ "$glibc_major" -gt "$required_major" ]; then
|
||||
return 0
|
||||
elif [ "$glibc_major" -eq "$required_major" ] && [ "$glibc_minor" -ge "$required_minor" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
set_artifact_suffix() {
|
||||
if [ "$(uname -s 2>/dev/null | cut -c 1-5)" = "Linux" ]; then
|
||||
# Native binaries require new enough GLIBC; fall back to JVM launcher if older
|
||||
if ! check_glibc_version; then
|
||||
return
|
||||
fi
|
||||
if [ "$(uname -m)" = "aarch64" ]; then ARTIFACT_SUFFIX="-native-linux-aarch64"
|
||||
else ARTIFACT_SUFFIX="-native-linux-amd64"; fi
|
||||
elif [ "$(uname)" = "Darwin" ]; then
|
||||
if [ "$(uname -m)" = "arm64" ]; then ARTIFACT_SUFFIX="-native-mac-aarch64"
|
||||
else ARTIFACT_SUFFIX="-native-mac-amd64"; fi
|
||||
else
|
||||
echo "This native mill launcher supports only Linux and macOS." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "$MILL_VERSION" in
|
||||
*"$MILL_NATIVE_SUFFIX")
|
||||
MILL_VERSION=${MILL_VERSION%"$MILL_NATIVE_SUFFIX"}
|
||||
set_artifact_suffix
|
||||
;;
|
||||
|
||||
*"$MILL_JVM_SUFFIX")
|
||||
MILL_VERSION=${MILL_VERSION%"$MILL_JVM_SUFFIX"}
|
||||
;;
|
||||
|
||||
*)
|
||||
case "$MILL_VERSION" in
|
||||
0.1.* | 0.2.* | 0.3.* | 0.4.* | 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.* | 0.12.*)
|
||||
;;
|
||||
*)
|
||||
set_artifact_suffix
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
MILL="${MILL_FINAL_DOWNLOAD_FOLDER}/$MILL_VERSION$ARTIFACT_SUFFIX"
|
||||
|
||||
# If not already downloaded, download it
|
||||
if [ ! -s "${MILL}" ] || [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then
|
||||
case $MILL_VERSION in
|
||||
0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.*)
|
||||
MILL_DOWNLOAD_SUFFIX=""
|
||||
MILL_DOWNLOAD_FROM_MAVEN=0
|
||||
;;
|
||||
0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M*)
|
||||
MILL_DOWNLOAD_SUFFIX="-assembly"
|
||||
MILL_DOWNLOAD_FROM_MAVEN=0
|
||||
;;
|
||||
*)
|
||||
MILL_DOWNLOAD_SUFFIX="-assembly"
|
||||
MILL_DOWNLOAD_FROM_MAVEN=1
|
||||
;;
|
||||
esac
|
||||
case $MILL_VERSION in
|
||||
0.12.0 | 0.12.1 | 0.12.2 | 0.12.3 | 0.12.4 | 0.12.5 | 0.12.6 | 0.12.7 | 0.12.8 | 0.12.9 | 0.12.10 | 0.12.11)
|
||||
MILL_DOWNLOAD_EXT="jar"
|
||||
;;
|
||||
0.12.*)
|
||||
MILL_DOWNLOAD_EXT="exe"
|
||||
;;
|
||||
0.*)
|
||||
MILL_DOWNLOAD_EXT="jar"
|
||||
;;
|
||||
*)
|
||||
MILL_DOWNLOAD_EXT="exe"
|
||||
;;
|
||||
esac
|
||||
|
||||
MILL_TEMP_DOWNLOAD_FILE="${MILL_OUTPUT_DIR:-out}/mill-temp-download"
|
||||
mkdir -p "$(dirname "${MILL_TEMP_DOWNLOAD_FILE}")"
|
||||
|
||||
if [ "$MILL_DOWNLOAD_FROM_MAVEN" = "1" ] ; then
|
||||
MILL_DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist${ARTIFACT_SUFFIX}/${MILL_VERSION}/mill-dist${ARTIFACT_SUFFIX}-${MILL_VERSION}.${MILL_DOWNLOAD_EXT}"
|
||||
else
|
||||
MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
|
||||
MILL_DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${MILL_DOWNLOAD_SUFFIX}"
|
||||
unset MILL_VERSION_TAG
|
||||
fi
|
||||
|
||||
|
||||
if [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then
|
||||
echo "$MILL_DOWNLOAD_URL"
|
||||
echo "$MILL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Downloading mill ${MILL_VERSION} from ${MILL_DOWNLOAD_URL} ..." 1>&2
|
||||
curl -f -L -o "${MILL_TEMP_DOWNLOAD_FILE}" "${MILL_DOWNLOAD_URL}"
|
||||
|
||||
chmod +x "${MILL_TEMP_DOWNLOAD_FILE}"
|
||||
|
||||
mkdir -p "${MILL_FINAL_DOWNLOAD_FOLDER}"
|
||||
mv "${MILL_TEMP_DOWNLOAD_FILE}" "${MILL}"
|
||||
|
||||
unset MILL_TEMP_DOWNLOAD_FILE
|
||||
unset MILL_DOWNLOAD_SUFFIX
|
||||
fi
|
||||
|
||||
MILL_FIRST_ARG=""
|
||||
if [ "$1" = "--bsp" ] || [ "${1#"-i"}" != "$1" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--no-daemon" ] || [ "$1" = "--help" ] ; then
|
||||
# Need to preserve the first position of those listed options
|
||||
MILL_FIRST_ARG=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
unset MILL_FINAL_DOWNLOAD_FOLDER
|
||||
unset MILL_OLD_DOWNLOAD_PATH
|
||||
unset OLD_MILL
|
||||
unset MILL_VERSION
|
||||
unset MILL_REPO_URL
|
||||
|
||||
# -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2
|
||||
# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes
|
||||
# shellcheck disable=SC2086
|
||||
exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"
|
||||
2
release-scripts/modrinth/.gitignore
vendored
Normal file
2
release-scripts/modrinth/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
.metals
|
||||
186
release-scripts/modrinth/ModrinthVersionUploader.java
Normal file
186
release-scripts/modrinth/ModrinthVersionUploader.java
Normal file
@@ -0,0 +1,186 @@
|
||||
//| mvnDeps:
|
||||
//| - com.google.code.gson:gson:2.13.2
|
||||
//| - org.yaml:snakeyaml:2.6
|
||||
//| - org.apache.httpcomponents.client5:httpclient5:5.6
|
||||
|
||||
import com.google.gson.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.logging.*;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.io.*;
|
||||
import org.apache.hc.client5.http.classic.*;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||
import org.apache.hc.client5.http.entity.mime.*;
|
||||
import org.apache.hc.client5.http.impl.classic.*;
|
||||
import org.apache.hc.core5.http.*;
|
||||
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||
|
||||
public class ModrinthVersionUploader {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ModrinthVersionUploader.class.getName());
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try (var client = new ModrinthClient(getAccessToken())) {
|
||||
var plugins = getPluginFiles();
|
||||
var versionInfo = createVersion(plugins);
|
||||
LOGGER.log(Level.INFO, "Uploading version: {0}", versionInfo);
|
||||
var response = client.createVersion(versionInfo, plugins);
|
||||
LOGGER.log(Level.INFO, "Got response from Modrinth API: {0}", response);
|
||||
}
|
||||
}
|
||||
|
||||
private static Version createVersion(List<Path> plugins) {
|
||||
String pluginVersion = readPluginVersion(plugins.getFirst());
|
||||
boolean isSnapshot = pluginVersion.endsWith("-SNAPSHOT");
|
||||
|
||||
String versionName = "InvSee++ " + pluginVersion;
|
||||
String versionNumber = pluginVersion;
|
||||
String changeLog = "Automated deployment for InvSee++ v" + pluginVersion;
|
||||
List<String> dependencies = List.of();
|
||||
List<String> gameVersions = List.of(getMinecraftVersions());
|
||||
String versionType = isSnapshot ? "alpha" : "release";
|
||||
List<String> loaders = List.of("bukkit", "spigot", "paper", "purpur");
|
||||
boolean featured = !isSnapshot;
|
||||
String status = "listed";
|
||||
String projectId = "bYazc7fd"; // obtained from https://api.modrinth.com/v2/project/invsee++;
|
||||
List<String> fileParts = plugins.stream()
|
||||
.map(path -> getFilePartName(path))
|
||||
.toList();
|
||||
String primaryFile = getFilePartName(plugins.getFirst());
|
||||
|
||||
return new Version(
|
||||
versionName,
|
||||
versionNumber,
|
||||
changeLog,
|
||||
dependencies,
|
||||
gameVersions,
|
||||
versionType,
|
||||
loaders,
|
||||
featured,
|
||||
status,
|
||||
projectId,
|
||||
fileParts,
|
||||
primaryFile
|
||||
);
|
||||
}
|
||||
|
||||
private static List<Path> getPluginFiles() {
|
||||
return List.of(
|
||||
Path.of("plugins/InvSee++.jar"),
|
||||
Path.of("plugins/InvSee++_Give.jar"),
|
||||
Path.of("plugins/InvSee++_Clear.jar")
|
||||
);
|
||||
}
|
||||
|
||||
static String getFilePartName(Path path) {
|
||||
String fileName = path.getFileName().toString();
|
||||
return fileName.substring(0, fileName.length() - 4); // omit ".jar" suffix
|
||||
// alternative implementation: read plugin name from 'name' or 'prefix' field from the plugin.yml in the jar file.
|
||||
}
|
||||
|
||||
private static String getAccessToken() {
|
||||
return System.getenv("MODRINTH_TOKEN");
|
||||
}
|
||||
|
||||
private static String[] getMinecraftVersions() {
|
||||
return System.getenv("MINECRAFT_VERSIONS").split(" ");
|
||||
}
|
||||
|
||||
private static String readPluginVersion(Path pluginFile) {
|
||||
try (ZipFile zipFile = new ZipFile(pluginFile.toFile())) {
|
||||
|
||||
ZipEntry entry = zipFile.getEntry("plugin.yml");
|
||||
|
||||
if (entry != null) {
|
||||
try (InputStream inputStream = zipFile.getInputStream(entry)) {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> data = yaml.load(inputStream);
|
||||
return (String) data.get("version");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
throw new RuntimeException("plugin.yml file inside plugin jar missing.");
|
||||
}
|
||||
}
|
||||
|
||||
class ModrinthClient implements AutoCloseable {
|
||||
|
||||
private static final String MODRINTH_BASE_URL = "https://api.modrinth.com/";
|
||||
private static final String USER_AGENT = "Jannyboy11/InvSee++/ModrinthVersionUploader";
|
||||
|
||||
private final CloseableHttpClient client;
|
||||
private final String token;
|
||||
private final Gson gson;
|
||||
|
||||
ModrinthClient(String token) {
|
||||
this.token = token;
|
||||
this.gson = new GsonBuilder()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.create();
|
||||
this.client = HttpClients.createDefault();
|
||||
}
|
||||
|
||||
// https://docs.modrinth.com/api/operations/createversion/
|
||||
VersionResponse createVersion(Version versionInfo, List<Path> files) throws Exception {
|
||||
HttpPost post = new HttpPost(MODRINTH_BASE_URL + "v2/version");
|
||||
post.addHeader("Authorization", token);
|
||||
post.addHeader("User-Agent", USER_AGENT);
|
||||
|
||||
String json = gson.toJson(versionInfo);
|
||||
|
||||
var entityBuilder = MultipartEntityBuilder.create()
|
||||
.setMode(HttpMultipartMode.STRICT)
|
||||
.addTextBody("data", json, ContentType.APPLICATION_JSON);
|
||||
for (Path file : files) {
|
||||
String fileName = file.getFileName().toString();
|
||||
String name = ModrinthVersionUploader.getFilePartName(file);
|
||||
entityBuilder.addBinaryBody(name, file, ContentType.DEFAULT_BINARY, fileName);
|
||||
}
|
||||
var entity = entityBuilder.build();
|
||||
|
||||
post.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse response = client.execute(post)) {
|
||||
String responseBody = EntityUtils.toString(response.getEntity());
|
||||
int status = response.getCode();
|
||||
if (status >= 400) {
|
||||
throw new RuntimeException("Modrinth API error (" + status + "): " + responseBody);
|
||||
}
|
||||
|
||||
return gson.fromJson(responseBody, VersionResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
record Version(
|
||||
String name,
|
||||
String versionNumber,
|
||||
String changeLog,
|
||||
List<String> dependencies,
|
||||
List<String> gameVersions,
|
||||
String versionType,
|
||||
List<String> loaders,
|
||||
boolean featured,
|
||||
String status,
|
||||
String projectId,
|
||||
List<String> fileParts,
|
||||
String primaryFile
|
||||
) { }
|
||||
|
||||
record VersionResponse(
|
||||
String id,
|
||||
String name,
|
||||
String versionNumber
|
||||
) { }
|
||||
Reference in New Issue
Block a user