mirror of
https://github.com/runelite/plugin-hub.git
synced 2025-12-23 14:39:11 -05:00
package: rewrite build scripts (#701)
not using bash gives us more flexibility and significantly reduces build times via parallelism. Additionally this includes support for GitHub Actions which also significantly reduces build times.
This commit is contained in:
58
.github/workflows/build.yml
vendored
Normal file
58
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
FORCE_BUILD:
|
||||
description: "List of plugins to build, or 'ALL'"
|
||||
required: false
|
||||
COMMIT_RANGE:
|
||||
description: "Commit range to build 1234abc..5689def"
|
||||
required: false
|
||||
push:
|
||||
pull_request:
|
||||
jobs:
|
||||
execute:
|
||||
# any forks that predate this repo having an action will have actions
|
||||
# enabled by default, which will fail in a lot of cases because the branch
|
||||
# is new, which makes the differential build fail
|
||||
if: github.event_name != 'push' || github.repository_owner == 'runelite'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/
|
||||
~/.gradle/wrapper/
|
||||
key: package-2.0.0
|
||||
- name: prepare
|
||||
run: |
|
||||
pushd package
|
||||
./gradlew --build-cache prep
|
||||
popd
|
||||
- name: build
|
||||
env:
|
||||
REPO_CREDS: ${{ secrets.REPO_CREDS }}
|
||||
REPO_ROOT: ${{ secrets.REPO_ROOT }}
|
||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||
# workflow_dispatch
|
||||
FORCE_BUILD: ${{ github.event.inputs.FORCE_BUILD }}
|
||||
COMMIT_RANGE: ${{ github.event.inputs.COMMIT_RANGE }}
|
||||
# push
|
||||
COMMIT_BEFORE: ${{ github.event.before }}
|
||||
COMMIT_AFTER: ${{ github.event.after }}
|
||||
# pull_request
|
||||
PR_BEFORE: ${{ github.event.pull_request.base.sha }}
|
||||
PR_AFTER: ${{ github.event.pull_request.head.sha }}
|
||||
PACKAGE_IS_PR: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
if $PACKAGE_IS_PR; then
|
||||
export PACKAGE_COMMIT_RANGE="$PR_BEFORE..$PR_AFTER"
|
||||
else
|
||||
export PACKAGE_COMMIT_RANGE="${COMMIT_RANGE:-${COMMIT_BEFORE:+$COMMIT_BEFORE..$COMMIT_AFTER}}"
|
||||
fi
|
||||
java -XX:+UseParallelGC -jar package/package/build/libs/package.jar
|
||||
12
.travis.yml
12
.travis.yml
@@ -1,10 +1,12 @@
|
||||
language: java
|
||||
os: linux
|
||||
dist: xenial
|
||||
virt: lxd
|
||||
dist: focal
|
||||
language: generic
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
install: true
|
||||
script: ./travis.sh
|
||||
script: ./package/travis.sh
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2019 Abex
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
source repo_config.sh
|
||||
|
||||
set -e -x
|
||||
|
||||
[[ "${TRAVIS_PULL_REQUEST:-false}" == "false" ]] || exit 0
|
||||
|
||||
RUNELITE_VERSION="$(cat "runelite.version")"
|
||||
|
||||
MANIFEST="$(mktemp /tmp/manifest.XXXXXXXX)"
|
||||
trap "rm -rf ""$MANIFEST*""" EXIT
|
||||
MANIFEST_DIR="$MANIFEST.sub/"
|
||||
mkdir "$MANIFEST_DIR"
|
||||
|
||||
MANIFEST_CHUNK_DOWNLOAD=()
|
||||
|
||||
for PLUGINFILE in plugins/*; do
|
||||
# read in the plugin descriptor
|
||||
disabled=
|
||||
# shellcheck disable=SC2162
|
||||
while read LINE || [[ -n "$LINE" ]]; do
|
||||
[[ $LINE =~ ^(repository|commit|disabled|warning)=(.*)$ ]]
|
||||
eval "${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
|
||||
done < "$PLUGINFILE"
|
||||
[ -z "$disabled" ] || continue
|
||||
|
||||
PLUGIN_ID=$(basename "$PLUGINFILE")
|
||||
LOCATION="$REPO_ROOT/$RUNELITE_VERSION/$PLUGIN_ID/$commit"
|
||||
MANIFEST_CHUNK_DOWNLOAD+=('--output' "$MANIFEST_DIR/$PLUGIN_ID" "$LOCATION.manifest")
|
||||
done
|
||||
|
||||
curl --fail --retry 5 \
|
||||
"${MANIFEST_CHUNK_DOWNLOAD[@]}" || true
|
||||
|
||||
IS_FIRST=true
|
||||
echo "[" > "$MANIFEST"
|
||||
for MANIFEST_CHUNK in "$MANIFEST_DIR"/*; do
|
||||
if [[ "$IS_FIRST" != true ]]; then
|
||||
echo "," >> "$MANIFEST"
|
||||
fi
|
||||
IS_FIRST=
|
||||
cat "$MANIFEST_CHUNK" >> "$MANIFEST"
|
||||
done
|
||||
echo "]" >> "$MANIFEST"
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
openssl dgst -sha256 -sign <(set +x; printf -- "$SIGNING_KEY") -out "$MANIFEST.sig" "$MANIFEST"
|
||||
|
||||
perl -e "print pack('N', -s \"$MANIFEST.sig\")" > "$MANIFEST.out"
|
||||
cat "$MANIFEST.sig" >> "$MANIFEST.out"
|
||||
cat "$MANIFEST" >> "$MANIFEST.out"
|
||||
|
||||
curl --fail --retry 5 \
|
||||
--user "$REPO_CREDS" \
|
||||
--upload-file "$MANIFEST.out" "$REPO_ROOT/$RUNELITE_VERSION/manifest.js"
|
||||
|
||||
echo "Build Success"
|
||||
107
build_plugin.sh
107
build_plugin.sh
@@ -1,107 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2019 Abex
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
source repo_config.sh
|
||||
|
||||
set -e -x
|
||||
|
||||
PLUGINFILE="$1"
|
||||
[ -s "$PLUGINFILE" ]
|
||||
PLUGIN_ID=$(basename "$PLUGINFILE")
|
||||
|
||||
# check valid plugin id
|
||||
[[ $PLUGIN_ID =~ ^[a-z0-9-]+$ ]]
|
||||
|
||||
SCRIPT_HOME="$(cd "$(dirname "$0")" ; pwd -P)"
|
||||
|
||||
RUNELITE_VERSION="$(cat "$SCRIPT_HOME/runelite.version")"
|
||||
|
||||
# read in the plugin descriptor
|
||||
disabled=
|
||||
# shellcheck disable=SC2162
|
||||
while read LINE || [[ -n "$LINE" ]]; do
|
||||
[[ $LINE =~ ^(repository|commit|disabled|warning)=(.*)$ ]]
|
||||
eval "${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
|
||||
done < "$PLUGINFILE"
|
||||
[ -z "$disabled" ] || exit 0
|
||||
|
||||
# must be a https github repo
|
||||
[[ $repository =~ ^https://github.com/.*\.git$ ]]
|
||||
|
||||
# we must have a full 40 char sha1sum
|
||||
[[ $commit =~ ^[a-fA-F0-9]{40}+$ ]]
|
||||
|
||||
# we need gradle 6.2+ for dependency verification
|
||||
GRADLE_VER=gradle-6.6.1
|
||||
if [[ ! -e "/tmp/$GRADLE_VER/bin/gradle" ]]; then
|
||||
wget -q -O/tmp/gradle.zip "https://services.gradle.org/distributions/$GRADLE_VER-bin.zip"
|
||||
echo '7873ed5287f47ca03549ab8dcb6dc877ac7f0e3d7b1eb12685161d10080910ac */tmp/gradle.zip' | shasum -a256 -c
|
||||
unzip -q /tmp/gradle.zip -d /tmp/
|
||||
[[ -e "/tmp/$GRADLE_VER/bin/gradle" ]]
|
||||
fi
|
||||
export GRADLE_HOME="/tmp/$GRADLE_VER/"
|
||||
export PATH="$GRADLE_HOME/bin:$PATH"
|
||||
|
||||
BUILDDIR="$(mktemp -d /tmp/external-plugin.XXXXXXXX)"
|
||||
trap "rm -rf ""$BUILDDIR""" EXIT
|
||||
pushd "$BUILDDIR"
|
||||
|
||||
git clone -c 'advice.detachedHead=false' "$repository" "repo"
|
||||
pushd "repo"
|
||||
git checkout "$commit^{commit}"
|
||||
|
||||
SIGNING_KEY="" REPO_CREDS="" gradle \
|
||||
--no-build-cache \
|
||||
--parallel \
|
||||
--console=plain \
|
||||
--init-script="$SCRIPT_HOME/package.gradle" \
|
||||
-DrlpluginRuneLiteVersion="$RUNELITE_VERSION" \
|
||||
-DrlpluginOutputDirectory="$BUILDDIR" \
|
||||
-DrlpluginPluginID="$PLUGIN_ID" \
|
||||
-DrlpluginCommit="$commit" \
|
||||
-DrlpluginWarning="$warning" \
|
||||
rlpluginPackageJar rlpluginEmitManifest
|
||||
|
||||
[ -s "$BUILDDIR/plugin.jar" ]
|
||||
[ -s "$BUILDDIR/plugin.manifest" ]
|
||||
|
||||
cat "$BUILDDIR/plugin.manifest"
|
||||
|
||||
[[ "${TRAVIS_PULL_REQUEST:-false}" == "false" ]] || exit 0
|
||||
|
||||
LOCATION="$REPO_ROOT/$RUNELITE_VERSION/$PLUGIN_ID/$commit"
|
||||
|
||||
ICON_UPLOAD=()
|
||||
if [ -e "icon.png" ]; then
|
||||
ICON_UPLOAD=("--upload-file" "icon.png" "$LOCATION.png")
|
||||
fi
|
||||
|
||||
curl --fail --retry 5 \
|
||||
--user "$REPO_CREDS" \
|
||||
--upload-file "$BUILDDIR/plugin.manifest" "$LOCATION.manifest" \
|
||||
--upload-file "$BUILDDIR/plugin.jar" "$LOCATION.jar" \
|
||||
"${ICON_UPLOAD[@]}"
|
||||
|
||||
echo "Build Success"
|
||||
161
package.gradle
161
package.gradle
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import com.google.common.hash.Hashing
|
||||
import com.google.common.io.Files
|
||||
import com.google.gson.Gson
|
||||
import java.util.jar.JarFile
|
||||
|
||||
initscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.github.jengelman.gradle.plugins:shadow:6.0.0"
|
||||
classpath "com.google.code.gson:gson:2.8.5"
|
||||
classpath "com.google.guava:guava:23.2-jre"
|
||||
}
|
||||
configurations.classpath.resolutionStrategy {
|
||||
// We don't have a way to add our direct deps to the metadata file,
|
||||
// so we disable it for the initscript's configuration
|
||||
disableDependencyVerification()
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: "java";
|
||||
apply plugin: com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
|
||||
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
|
||||
task rlpluginPackageJar(type: ShadowJar) {
|
||||
destinationDir = new File(System.properties["rlpluginOutputDirectory"])
|
||||
archiveName = "plugin.jar"
|
||||
configurations = [project.configurations.runtimeClasspath]
|
||||
from sourceSets.main.output
|
||||
}
|
||||
|
||||
task rlpluginEmitManifest {
|
||||
// this doesn't have up-to-date stuff because we always do --no-build-cache
|
||||
|
||||
doLast {
|
||||
def manifest = new ExternalPluginManifest()
|
||||
manifest.internalName = System.properties["rlpluginPluginID"]
|
||||
manifest.commit = System.properties["rlpluginCommit"]
|
||||
if (System.properties["rlpluginWarning"]) {
|
||||
manifest.warning = System.properties["rlpluginWarning"];
|
||||
}
|
||||
|
||||
def pluginJar = new File(System.properties["rlpluginOutputDirectory"], "plugin.jar");
|
||||
manifest.hash = Files.asByteSource(pluginJar)
|
||||
.hash(Hashing.sha256())
|
||||
.toString()
|
||||
manifest.size = pluginJar.length()
|
||||
|
||||
if (manifest.size > 10 * 1024 * 1024) {
|
||||
throw new RuntimeException("The output jar is ${manifest.size.intdiv(1024 * 1024)}MiB, which is above our limit of 10MiB")
|
||||
}
|
||||
|
||||
def props = new Properties()
|
||||
new FileInputStream(file("runelite-plugin.properties")).withCloseable { is ->
|
||||
props.load(is)
|
||||
}
|
||||
|
||||
manifest.plugins = props["plugins"].split(/[,:;]/)*.trim()
|
||||
new JarFile(pluginJar).withCloseable{ jf ->
|
||||
manifest.plugins.each { plugin ->
|
||||
if (jf.getEntry(plugin.replaceAll(~/\./, "/") + ".class") == null) {
|
||||
throw new RuntimeException("Plugin class \"" + plugin + "\" is not in the output jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest.displayName = props["displayName"]
|
||||
if (!manifest.displayName) {
|
||||
throw new RuntimeException("Plugin must have a display name")
|
||||
}
|
||||
manifest.author = props["author"]
|
||||
if (!manifest.author) {
|
||||
throw new RuntimeException("Plugin must have an author")
|
||||
}
|
||||
if (props["support"]) {
|
||||
manifest.support = new URL(props["support"])
|
||||
}
|
||||
manifest.description = props["description"] ?: null
|
||||
if (props["tags"]) {
|
||||
manifest.tags = props["tags"].split(",")*.trim()
|
||||
}
|
||||
|
||||
manifest.version = project.version
|
||||
if (!(manifest.version ==~ /^[a-zA-Z0-9.-]+$/)) {
|
||||
throw new RuntimeException("Plugin version \"${manifest.version}\" is invalid");
|
||||
}
|
||||
|
||||
manifest.hasIcon = file("icon.png").exists();
|
||||
|
||||
new File(System.properties["rlpluginOutputDirectory"], "plugin.manifest")
|
||||
.text = new Gson().toJson(manifest);
|
||||
}
|
||||
}
|
||||
|
||||
rlpluginEmitManifest.dependsOn rlpluginPackageJar
|
||||
|
||||
task configured {
|
||||
def runeLiteDeps = [
|
||||
"client",
|
||||
"runelite-api",
|
||||
"http-api",
|
||||
]
|
||||
def version = System.properties["rlpluginRuneLiteVersion"]
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == "net.runelite" && details.requested.name in runeLiteDeps) {
|
||||
details.useVersion version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalPluginManifest {
|
||||
String internalName;
|
||||
String commit;
|
||||
String hash;
|
||||
int size;
|
||||
String[] plugins;
|
||||
|
||||
String displayName;
|
||||
String version;
|
||||
String author;
|
||||
String description;
|
||||
String warning;
|
||||
String[] tags;
|
||||
URL support;
|
||||
boolean hasIcon;
|
||||
}
|
||||
4
package/.gitignore
vendored
Normal file
4
package/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.gradle/
|
||||
.idea/
|
||||
build
|
||||
profile.svg
|
||||
59
package/build.gradle
Normal file
59
package/build.gradle
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
plugins {
|
||||
id "com.github.johnrengelman.shadow" version "6.1.0"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
version "2.0-SNAPSHOT"
|
||||
group "net.runelite.pluginhub"
|
||||
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "java"
|
||||
apply plugin: "com.github.johnrengelman.shadow"
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
shadowJar {
|
||||
archiveFileName.set archiveBaseName.get() + "." + archiveExtension.get()
|
||||
}
|
||||
}
|
||||
|
||||
// we have this task then java -jar the output rather than using gradle run so
|
||||
// the daemon used to build this can be reused by a real plugin build
|
||||
task prep {
|
||||
dependsOn ":package:shadowJar"
|
||||
dependsOn ":initLib:shadowJar"
|
||||
doLast {
|
||||
file("build").mkdirs()
|
||||
file("build/gradleHome").write(gradle.gradleHomeDir.absolutePath)
|
||||
}
|
||||
}
|
||||
2
package/gradle.properties
Normal file
2
package/gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
# this should match what Plugin uses to build with
|
||||
org.gradle.jvmargs=-Xmx768M -XX:+UseParallelGC
|
||||
373
package/gradle/verification-metadata.xml
Normal file
373
package/gradle/verification-metadata.xml
Normal file
@@ -0,0 +1,373 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.0.xsd">
|
||||
<configuration>
|
||||
<verify-metadata>true</verify-metadata>
|
||||
<verify-signatures>false</verify-signatures>
|
||||
<trusted-artifacts>
|
||||
<trust file=".*-javadoc[.]jar" regex="true"/>
|
||||
<trust file=".*-sources[.]jar" regex="true"/>
|
||||
</trusted-artifacts>
|
||||
</configuration>
|
||||
<components>
|
||||
<component group="com.github.jengelman.gradle.plugins" name="shadow" version="6.1.0">
|
||||
<artifact name="shadow-6.1.0.jar">
|
||||
<sha256 value="b66cb33a1d204ffaa1ba67393bdddbe9ff517f24f4438d11c341423868759aa3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="shadow-6.1.0.pom">
|
||||
<sha256 value="d40c29bce31762b6c8539a87d2515324f44db9d7d579a5aa7016a15ce164abb8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.johnrengelman.shadow" name="com.github.johnrengelman.shadow.gradle.plugin" version="6.1.0">
|
||||
<artifact name="com.github.johnrengelman.shadow.gradle.plugin-6.1.0.pom">
|
||||
<sha256 value="d79cec882e8f6870d9872bc10d1a4f80630308b64a91ba0630cd5e1fb4dfd05b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
|
||||
<artifact name="jsr305-3.0.2.jar">
|
||||
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jsr305-3.0.2.pom">
|
||||
<sha256 value="19889dbdf1b254b2601a5ee645b8147a974644882297684c798afe5d63d78dfe" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson" version="2.8.5">
|
||||
<artifact name="gson-2.8.5.jar">
|
||||
<sha256 value="233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="gson-2.8.5.pom">
|
||||
<sha256 value="b8308557a7fccc92d9fe7c8cd0599258b361285d2ecde7689eda98843255a092" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson-parent" version="2.8.5">
|
||||
<artifact name="gson-parent-2.8.5.pom">
|
||||
<sha256 value="8f1fec72b91a71ea39ec39f5f778c4d1124b6b097c6d55b3a50b554a52237b27" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.0.18">
|
||||
<artifact name="error_prone_annotations-2.0.18.jar">
|
||||
<sha256 value="cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="error_prone_annotations-2.0.18.pom">
|
||||
<sha256 value="9144127192d6f612c2366825dceaeb23b0d53130b83e0bf1ffe107d1470a8487" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_parent" version="2.0.18">
|
||||
<artifact name="error_prone_parent-2.0.18.pom">
|
||||
<sha256 value="cf149955279b07d4f11e817985c1164a69e930d73db7441b43a6ef53bbd286c4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="guava" version="23.2-jre">
|
||||
<artifact name="guava-23.2-jre.jar">
|
||||
<sha256 value="5be9a7d05ba0ccd74708bc8018ae412255f85843c0b92302e9b9befa6ed52564" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="guava-23.2-jre.pom">
|
||||
<sha256 value="57abac7e3962e45ad1c8645d91cdad96e1e0bf23b8b38e6f389011f7ae496870" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="guava-parent" version="23.2-jre">
|
||||
<artifact name="guava-parent-23.2-jre.pom">
|
||||
<sha256 value="e92cd52f3b86a0f6fe90fc94a86ea072a2b112fc40063bd33f6679778038cc47" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.j2objc" name="j2objc-annotations" version="1.1">
|
||||
<artifact name="j2objc-annotations-1.1.jar">
|
||||
<sha256 value="2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="j2objc-annotations-1.1.pom">
|
||||
<sha256 value="f0c98c571e93a7cb4dd18df0fa308f0963e7a0620ac2d4244e61e709d03ad6be" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="mockwebserver" version="3.14.9">
|
||||
<artifact name="mockwebserver-3.14.9.jar">
|
||||
<sha256 value="099f70ab2997f628bf93c358baac829ccb625b6e08a9361e09d6923fa9af1f68" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="mockwebserver-3.14.9.pom">
|
||||
<sha256 value="24fc2ed2c0d80c7461e24f4dc3f873381a5a77246cf99a57988d1b389b091fd6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="okhttp" version="3.14.9">
|
||||
<artifact name="okhttp-3.14.9.jar">
|
||||
<sha256 value="2570fab55515cbf881d7a4ceef49fc515490bc027057e666776a2832465aeca0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="okhttp-3.14.9.pom">
|
||||
<sha256 value="61edf3c4764ea95f1212ca2dfbc6ea5d4133c71c4e8c40bd247ec83e7c6b7ce7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="parent" version="3.14.9">
|
||||
<artifact name="parent-3.14.9.pom">
|
||||
<sha256 value="b5f101dcf2cc7f32ec3d7b68bdc078e77eac1979bb8664e8f87f762288c3dce5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okio" name="okio" version="1.17.2">
|
||||
<artifact name="okio-1.17.2.jar">
|
||||
<sha256 value="f80ce42d2ffac47ad4c47e1d6f980d604d247ceb1a886705cf4581ab0c9fe2b8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="okio-1.17.2.pom">
|
||||
<sha256 value="cd57f75443ab6a714b203da51994ee64ed0e919fea81f04e4f4b5324e4bb03f0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okio" name="okio-parent" version="1.17.2">
|
||||
<artifact name="okio-parent-1.17.2.pom">
|
||||
<sha256 value="e81f40146f2a0eb394b10fa3a175c85ab9fbe757aa418aa44d4b760efe16f024" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="commons-io" name="commons-io" version="2.5">
|
||||
<artifact name="commons-io-2.5.pom">
|
||||
<sha256 value="28ebb2998bc7d7acb25078526971640892000f3413586ff42d611f1043bfec30" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="commons-io" name="commons-io" version="2.6">
|
||||
<artifact name="commons-io-2.6.jar">
|
||||
<sha256 value="f877d304660ac2a142f3865badfc971dec7ed73c747c7f8d5d2f5139ca736513" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="commons-io-2.6.pom">
|
||||
<sha256 value="0c23863893a2291f5a7afdbd8d15923b3948afd87e563fa341cdcf6eae338a60" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="junit" name="junit" version="4.12">
|
||||
<artifact name="junit-4.12.jar">
|
||||
<sha256 value="59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="junit-4.12.pom">
|
||||
<sha256 value="90f163f78e3ffb6f1c7ad97de9e7eba4eea25807141b85d6d12be67ca25449c4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache" name="apache" version="16">
|
||||
<artifact name="apache-16.pom">
|
||||
<sha256 value="9f85ff2fd7d6cb3097aa47fb419ee7f0ebe869109f98aba9f4eca3f49e74a40e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache" name="apache" version="18">
|
||||
<artifact name="apache-18.pom">
|
||||
<sha256 value="7831307285fd475bbc36b20ae38e7882f11c3153b1d5930f852d44eda8f33c17" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache" name="apache" version="21">
|
||||
<artifact name="apache-21.pom">
|
||||
<sha256 value="af10c108da014f17cafac7b52b2b4b5a3a1c18265fa2af97a325d9143537b380" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.ant" name="ant" version="1.9.7">
|
||||
<artifact name="ant-1.9.7.jar">
|
||||
<sha256 value="9a5dbe3f5f2cb91854c8682cab80178afa412ab35a5ab718bf39ce01b3435d93" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="ant-1.9.7.pom">
|
||||
<sha256 value="1b9fbd4f325a71e99b279080d63084f12d884d42081af298f9e553e1fe0cd74a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.ant" name="ant-launcher" version="1.9.7">
|
||||
<artifact name="ant-launcher-1.9.7.jar">
|
||||
<sha256 value="bc376f6d6cb586229f451ac459faf1443b144c26d6647618ec9cba60e54c2b79" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="ant-launcher-1.9.7.pom">
|
||||
<sha256 value="d7bcdd3ab0ff55edbe1b96d06f06dac2135ec63b5a7c32cef3a436b49c9eee27" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.ant" name="ant-parent" version="1.9.7">
|
||||
<artifact name="ant-parent-1.9.7.pom">
|
||||
<sha256 value="75d2cef64c65ccbdd2faf7261e53b444778d56d338763154e30fada4a41d1215" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.commons" name="commons-parent" version="39">
|
||||
<artifact name="commons-parent-39.pom">
|
||||
<sha256 value="87cd27e1a02a5c3eb6d85059ce98696bb1b44c2b8b650f0567c86df60fa61da7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.commons" name="commons-parent" version="42">
|
||||
<artifact name="commons-parent-42.pom">
|
||||
<sha256 value="cd313494c670b483ec256972af1698b330e598f807002354eb765479f604b09c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.logging.log4j" name="log4j" version="2.13.3">
|
||||
<artifact name="log4j-2.13.3.pom">
|
||||
<sha256 value="674f1fa5165b9d48935f4103d9316fe5b161dff6f9be904a6edb9baa33da4480" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.logging.log4j" name="log4j-api" version="2.13.3">
|
||||
<artifact name="log4j-api-2.13.3.jar">
|
||||
<sha256 value="2b4b1965c9dce7f3732a0fbf5c8493199c1e6bf8cf65c3e235b57d98da5f36af" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="log4j-api-2.13.3.pom">
|
||||
<sha256 value="5953807d4e4fd4d7ae8087b5a76660236e55e718fcad62cf8a7adedc2ddc5a6e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.logging.log4j" name="log4j-core" version="2.13.3">
|
||||
<artifact name="log4j-core-2.13.3.jar">
|
||||
<sha256 value="9529c55814264ab96b0eeba2920ac0805170969c994cc479bd3d4d7eb24a35a8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="log4j-core-2.13.3.pom">
|
||||
<sha256 value="ec5592381a9b37e5054a91fcaf79e3c2c4582eee3574d9ad8a022afbd5b5a3fb" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus" name="codehaus-parent" version="4">
|
||||
<artifact name="codehaus-parent-4.pom">
|
||||
<sha256 value="6b87237de8c2e1740cf80627c7f3ce3e15de1930bb250c55a1eca94fa3e014df" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="animal-sniffer-annotations" version="1.14">
|
||||
<artifact name="animal-sniffer-annotations-1.14.jar">
|
||||
<sha256 value="2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="animal-sniffer-annotations-1.14.pom">
|
||||
<sha256 value="1879f19a05991e3ed95910b96689333396b0c467a215dc4d1f90018404b72a26" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="animal-sniffer-parent" version="1.14">
|
||||
<artifact name="animal-sniffer-parent-1.14.pom">
|
||||
<sha256 value="f51550a06b1410bd4962cb0e71df0b921a60a7ef47bfa9c4825a14be72316eea" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="mojo-parent" version="34">
|
||||
<artifact name="mojo-parent-34.pom">
|
||||
<sha256 value="3e395d6fbc43c09a3774cac8694ce527398305ea3fd5492d80e25af27d382a9c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.plexus" name="plexus" version="4.0">
|
||||
<artifact name="plexus-4.0.pom">
|
||||
<sha256 value="0a1b692d7fcc90d6a45dae2e50f4660d48f7a44504f174aa60ef34fbe1327f6a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.plexus" name="plexus-utils" version="3.0.24">
|
||||
<artifact name="plexus-utils-3.0.24.jar">
|
||||
<sha256 value="83ee748b12d06afb0ad4050a591132b3e8025fbb1990f1ed002e8b73293e69b4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="plexus-utils-3.0.24.pom">
|
||||
<sha256 value="11067f6a75fded12bcdc8daf7a66ddd942ce289c3daf88a3fe0f8b12858a2ee6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.gradle" name="gradle-tooling-api" version="6.6.1">
|
||||
<artifact name="gradle-tooling-api-6.6.1.jar">
|
||||
<sha256 value="38b7a9317a06a0f3797f972462d198d8b370faa1aecc5832fba736c4c99aa3b2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="gradle-tooling-api-6.6.1.module">
|
||||
<sha256 value="41817723d2b2ff73f7fde1e7a927593a201b31348bcbfaff8322dbdf0d3f38b6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.hamcrest" name="hamcrest-core" version="1.3">
|
||||
<artifact name="hamcrest-core-1.3.jar">
|
||||
<sha256 value="66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="hamcrest-core-1.3.pom">
|
||||
<sha256 value="fde386a7905173a1b103de6ab820727584b50d0e32282e2797787c20a64ffa93" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.hamcrest" name="hamcrest-parent" version="1.3">
|
||||
<artifact name="hamcrest-parent-1.3.pom">
|
||||
<sha256 value="6d535f94efb663bdb682c9f27a50335394688009642ba7a9677504bc1be4129b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jdom" name="jdom2" version="2.0.6">
|
||||
<artifact name="jdom2-2.0.6.jar">
|
||||
<sha256 value="1345f11ba606d15603d6740551a8c21947c0215640770ec67271fe78bea97cf5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jdom2-2.0.6.pom">
|
||||
<sha256 value="47b23a79fe336b741b82434c6e049d68165256e405e75c10921fd72fa8a65d8d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2" name="ow2" version="1.5">
|
||||
<artifact name="ow2-1.5.pom">
|
||||
<sha256 value="0f8a1b116e760b8fe6389c51b84e4b07a70fc11082d4f936e453b583dd50b43b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm" version="7.0">
|
||||
<artifact name="asm-7.0.jar">
|
||||
<sha256 value="b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-7.0.pom">
|
||||
<sha256 value="83f65b1083d5ce4f8ba7f9545cfe9ff17824589c9a7cc82c3a4695801e4f5f68" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm" version="9.0">
|
||||
<artifact name="asm-9.0.jar">
|
||||
<sha256 value="0df97574914aee92fd349d0cb4e00f3345d45b2c239e0bb50f0a90ead47888e0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-9.0.module">
|
||||
<sha256 value="8af81096ed3affa39a4729fc900a55b663894911d67c4d4bef0ea424393dd3f9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm-analysis" version="9.0">
|
||||
<artifact name="asm-analysis-9.0.jar">
|
||||
<sha256 value="2d46de6df856a4daac9aa534459ab7287eb80584e9109850405e5b302dc9c2a6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-analysis-9.0.module">
|
||||
<sha256 value="6e02aafb4637979c7cdc3daa3047a88b40f3e1071bdbbd0c7f1cd5da7ac38454" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm-commons" version="9.0">
|
||||
<artifact name="asm-commons-9.0.jar">
|
||||
<sha256 value="1b9090acb7e67bd4ed2f2cfb002063316d79cecace237bd07cc4f7f1b302092f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-commons-9.0.module">
|
||||
<sha256 value="58880e03e9196f566c998186f58bd0af41c77a04ba841664f80377ba0665f97c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm-tree" version="9.0">
|
||||
<artifact name="asm-tree-9.0.jar">
|
||||
<sha256 value="e2c25f332eb95861883a8568e45aac5e77d140d0fe961ae8eb9a474ec876e00d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-tree-9.0.module">
|
||||
<sha256 value="b38cbdd2c47fa4f29ab680c18954c7216d0afd28692221cd288c1cc7b9d9641c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.projectlombok" name="lombok" version="1.18.4">
|
||||
<artifact name="lombok-1.18.4.jar">
|
||||
<sha256 value="39f3922deb679b1852af519eb227157ef2dd0a21eec3542c8ce1b45f2df39742" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="lombok-1.18.4.pom">
|
||||
<sha256 value="2eaf8fc3d7a9c2ce18ec874bc4674f7ab492b2121143342321e820eea12ffa2f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-api" version="1.7.10">
|
||||
<artifact name="slf4j-api-1.7.10.jar">
|
||||
<sha256 value="3863e27005740d4d1289bf87b113efea115e9a22408a7d623be8004991232bfe" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-api-1.7.10.pom">
|
||||
<sha256 value="af9c5e8d2263422c74792ddd91b3cc1a24bd02b451b54cbb10cd6f2ba46c14b1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-api" version="1.7.28">
|
||||
<artifact name="slf4j-api-1.7.28.jar">
|
||||
<sha256 value="fb6e4f67a2a4689e3e713584db17a5d1090c1ebe6eec30e9e0349a6ee118141e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-api-1.7.28.pom">
|
||||
<sha256 value="61f10feac576665b68caa6170cd423e8fb00055f1fad7ad9d7de2150e5f15caa" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-parent" version="1.7.10">
|
||||
<artifact name="slf4j-parent-1.7.10.pom">
|
||||
<sha256 value="1abee7f5182fb79b4926fb64658af8c3248ed6b374f9ac7da1fd9e8b9197e2ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-parent" version="1.7.28">
|
||||
<artifact name="slf4j-parent-1.7.28.pom">
|
||||
<sha256 value="919b5f42dde33ace036865d1e2b292d98a0627417ff756e0287f4a56ad3e544e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-simple" version="1.7.10">
|
||||
<artifact name="slf4j-simple-1.7.10.jar">
|
||||
<sha256 value="3209a859bb47873c0d1873f7d69aa6652dc07fe32ed935492fe0b1cf1d2cf140" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-simple-1.7.10.pom">
|
||||
<sha256 value="bbd8c6f5cf54d1c1fb11b415e51f6b35483fc264f81ccd98654cc3420e756901" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.sonatype.forge" name="forge-parent" version="10">
|
||||
<artifact name="forge-parent-10.pom">
|
||||
<sha256 value="c14fb9c32b59cc03251f609416db7c0cff01f811edcccb4f6a865d6e7046bd0b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.sonatype.oss" name="oss-parent" version="7">
|
||||
<artifact name="oss-parent-7.pom">
|
||||
<sha256 value="b51f8867c92b6a722499557fc3a1fdea77bdf9ef574722fe90ce436a29559454" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.vafer" name="jdependency" version="2.1.1">
|
||||
<artifact name="jdependency-2.1.1.jar">
|
||||
<sha256 value="642d23a86217850721d9fa80671683d8308fd03114f0da7af553d43b82013a09" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jdependency-2.1.1.pom">
|
||||
<sha256 value="4a139306cbe0aa3765bd9fd837a71253a911a9c4e55c50e062a4bd6843ee19a1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
</components>
|
||||
</verification-metadata>
|
||||
BIN
package/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
package/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
package/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
package/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=11657af6356b7587bfb37287b5992e94a9686d5c8a0a1b60b87b9928a2decde5
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
172
package/gradlew
vendored
Executable file
172
package/gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
package/gradlew.bat
vendored
Normal file
84
package/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
32
package/initLib/build.gradle
Normal file
32
package/initLib/build.gradle
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:6.1.0"
|
||||
}
|
||||
59
package/package/build.gradle
Normal file
59
package/package/build.gradle
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo.gradle.org/gradle/libs-releases-local/"
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.gradle:gradle-tooling-api:6.6.1"
|
||||
implementation "org.slf4j:slf4j-simple:1.7.10"
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
implementation "com.google.guava:guava:23.2-jre"
|
||||
implementation "org.ow2.asm:asm:7.0"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.14.9"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
|
||||
def lombok = "org.projectlombok:lombok:1.18.4";
|
||||
compileOnly lombok
|
||||
annotationProcessor lombok
|
||||
testCompileOnly lombok
|
||||
testAnnotationProcessor lombok
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.9"
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes "Main-Class": "net.runelite.pluginhub.packager.Packager"
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
workingDir new File(project.rootDir, "../")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class DisabledPluginException extends Exception
|
||||
{
|
||||
@Getter
|
||||
private final String internalName;
|
||||
|
||||
public DisabledPluginException(String internalName, String cause)
|
||||
{
|
||||
super("Plugin \"" + internalName + "\" is disabled: " + cause);
|
||||
this.internalName = internalName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import java.net.URL;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
public class ExternalPluginManifest
|
||||
{
|
||||
private String internalName;
|
||||
private String commit;
|
||||
private String hash;
|
||||
private int size;
|
||||
private String[] plugins;
|
||||
|
||||
private String displayName;
|
||||
private String version;
|
||||
private String author;
|
||||
@Nullable
|
||||
private String description;
|
||||
@Nullable
|
||||
private String warning;
|
||||
@Nullable
|
||||
private String[] tags;
|
||||
@EqualsAndHashCode.Exclude
|
||||
private URL support;
|
||||
private boolean hasIcon;
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSource;
|
||||
|
||||
@Slf4j
|
||||
public class Packager
|
||||
{
|
||||
private static final File PLUGIN_ROOT = new File("./plugins");
|
||||
public static final File PACKAGE_ROOT = new File("./package/").getAbsoluteFile();
|
||||
|
||||
private Semaphore downloadSemaphore = new Semaphore(2);
|
||||
private Semaphore buildSemaphore = new Semaphore(Runtime.getRuntime().availableProcessors());
|
||||
private Semaphore uploadSemaphore = new Semaphore(2);
|
||||
|
||||
private final List<File> buildList;
|
||||
|
||||
@Getter
|
||||
private final String runeliteVersion;
|
||||
|
||||
@Getter
|
||||
private final UploadConfiguration uploadConfig = new UploadConfiguration();
|
||||
|
||||
private final AtomicInteger numDone = new AtomicInteger(0);
|
||||
private final int numTotal;
|
||||
|
||||
@Setter
|
||||
private boolean ignoreOldManifest;
|
||||
|
||||
@Setter
|
||||
private boolean alwaysPrintLog;
|
||||
|
||||
public Packager(List<File> buildList) throws IOException
|
||||
{
|
||||
this.buildList = buildList;
|
||||
this.numTotal = buildList.size();
|
||||
this.runeliteVersion = readRLVersion();
|
||||
}
|
||||
|
||||
Set<ExternalPluginManifest> newManifests = Sets.newConcurrentHashSet();
|
||||
Set<String> remove = Sets.newConcurrentHashSet();
|
||||
|
||||
public void buildPlugins()
|
||||
throws IOException, InvalidKeyException, NoSuchAlgorithmException, SignatureException
|
||||
{
|
||||
Queue<File> buildQueue = Queues.synchronizedQueue(new ArrayDeque<>(buildList));
|
||||
List<Thread> buildThreads = IntStream.range(0, 8)
|
||||
.mapToObj(v ->
|
||||
{
|
||||
Thread t = new Thread(() ->
|
||||
{
|
||||
for (File plugin; (plugin = buildQueue.poll()) != null; )
|
||||
{
|
||||
buildPlugin(plugin);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
return t;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
for (Thread buildThread : buildThreads)
|
||||
{
|
||||
try
|
||||
{
|
||||
buildThread.join();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
Gson gson = new Gson();
|
||||
HttpUrl manifestURL = uploadConfig.getUploadRepoRoot().newBuilder()
|
||||
.addPathSegment("manifest.js")
|
||||
.build();
|
||||
|
||||
List<ExternalPluginManifest> manifests = new ArrayList<>();
|
||||
if (!ignoreOldManifest)
|
||||
{
|
||||
try (Response res = uploadConfig.getClient().newCall(new Request.Builder()
|
||||
.url(manifestURL)
|
||||
.get()
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
if (res.code() != 404)
|
||||
{
|
||||
Util.check(res);
|
||||
|
||||
BufferedSource src = res.body().source();
|
||||
|
||||
byte[] signature = new byte[src.readInt()];
|
||||
src.readFully(signature);
|
||||
|
||||
byte[] data = src.readByteArray();
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initVerify(uploadConfig.getCert());
|
||||
s.update(data);
|
||||
|
||||
if (!s.verify(signature))
|
||||
{
|
||||
throw new RuntimeException("Unable to verify external plugin manifest");
|
||||
}
|
||||
|
||||
manifests = gson.fromJson(new String(data, StandardCharsets.UTF_8),
|
||||
new TypeToken<List<ExternalPluginManifest>>()
|
||||
{
|
||||
}.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifests.removeIf(m -> remove.contains(m.getInternalName()));
|
||||
manifests.addAll(newManifests);
|
||||
manifests.sort(Comparator.comparing(ExternalPluginManifest::getInternalName));
|
||||
|
||||
{
|
||||
byte[] data = gson.toJson(manifests).getBytes(StandardCharsets.UTF_8);
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initSign(uploadConfig.getKey());
|
||||
s.update(data);
|
||||
byte[] sig = s.sign();
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
new DataOutputStream(out).writeInt(sig.length);
|
||||
out.write(sig);
|
||||
out.write(data);
|
||||
byte[] manifest = out.toByteArray();
|
||||
|
||||
try (Response res = uploadConfig.getClient().newCall(new Request.Builder()
|
||||
.url(manifestURL)
|
||||
.put(RequestBody.create(null, manifest))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Util.check(res);
|
||||
}
|
||||
}
|
||||
|
||||
uploadConfig.getClient().connectionPool().evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void buildPlugin(File plugin)
|
||||
{
|
||||
remove.add(plugin.getName());
|
||||
|
||||
if (!plugin.exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try (Plugin p = new Plugin(plugin))
|
||||
{
|
||||
try
|
||||
{
|
||||
try (Closeable ignored = acquireDownload(p))
|
||||
{
|
||||
p.download();
|
||||
}
|
||||
try (Closeable ignored = acquireBuild(p))
|
||||
{
|
||||
p.build(runeliteVersion);
|
||||
p.assembleManifest();
|
||||
}
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
try (Closeable ignored = acquireUpload(p))
|
||||
{
|
||||
p.upload(uploadConfig);
|
||||
}
|
||||
|
||||
// outside the semaphore so the timing gets uploaded too
|
||||
p.uploadLog(uploadConfig);
|
||||
}
|
||||
|
||||
newManifests.add(p.getManifest());
|
||||
log.info("{}: done in {}ms [{}/{}]", p.getInternalName(), p.getBuildTimeMS(), numDone.get() + 1, numTotal);
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
p.writeLog("package failed\n", e);
|
||||
if (!alwaysPrintLog)
|
||||
{
|
||||
Files.asCharSource(p.getLogFile(), StandardCharsets.UTF_8).copyTo(System.out);
|
||||
}
|
||||
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
p.uploadLog(uploadConfig);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (alwaysPrintLog)
|
||||
{
|
||||
Files.asCharSource(p.getLogFile(), StandardCharsets.UTF_8).copyTo(System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DisabledPluginException e)
|
||||
{
|
||||
log.info("{}", e.getMessage());
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("{}: crashed the build script: ", plugin.getName(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
numDone.addAndGet(1);
|
||||
}
|
||||
}
|
||||
|
||||
private Closeable acquireDownload(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "download", downloadSemaphore);
|
||||
}
|
||||
|
||||
private Closeable acquireBuild(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "build", buildSemaphore);
|
||||
}
|
||||
|
||||
private Closeable acquireUpload(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "upload", uploadSemaphore);
|
||||
}
|
||||
|
||||
private Closeable section(Plugin p, String name, Semaphore s)
|
||||
{
|
||||
try
|
||||
{
|
||||
s.acquire();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Stopwatch time = Stopwatch.createStarted();
|
||||
return () ->
|
||||
{
|
||||
long ms = time.stop()
|
||||
.elapsed(TimeUnit.MILLISECONDS);
|
||||
p.setBuildTimeMS(p.getBuildTimeMS() + ms);
|
||||
p.writeLog("{}: {}ms\n", name, ms);
|
||||
s.release();
|
||||
};
|
||||
}
|
||||
|
||||
public static String readRLVersion() throws IOException
|
||||
{
|
||||
return Files.asCharSource(new File("./runelite.version"), StandardCharsets.UTF_8).read().trim();
|
||||
}
|
||||
|
||||
|
||||
public static void main(String... args) throws Exception
|
||||
{
|
||||
boolean isBuildingAll = false;
|
||||
List<File> buildList;
|
||||
if (args.length != 0)
|
||||
{
|
||||
buildList = Stream.of(args)
|
||||
.map(File::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
else if ("ALL".equals(System.getenv("FORCE_BUILD")))
|
||||
{
|
||||
buildList = listAllPlugins();
|
||||
}
|
||||
else if (!Strings.isNullOrEmpty(System.getenv("FORCE_BUILD")))
|
||||
{
|
||||
buildList = StreamSupport.stream(
|
||||
Splitter.on(',')
|
||||
.trimResults()
|
||||
.omitEmptyStrings()
|
||||
.split(System.getenv("FORCE_BUILD"))
|
||||
.spliterator(), false)
|
||||
.map(name -> new File(PLUGIN_ROOT, name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
else if (!Strings.isNullOrEmpty(System.getenv("PACKAGE_COMMIT_RANGE")))
|
||||
{
|
||||
Process gitdiff = new ProcessBuilder("git", "diff", "--name-only", System.getenv("PACKAGE_COMMIT_RANGE"))
|
||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
.start();
|
||||
|
||||
boolean doAll = false;
|
||||
boolean doPackageTests = false;
|
||||
buildList = new ArrayList<>();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(gitdiff.getInputStream())))
|
||||
{
|
||||
for (String line; (line = br.readLine()) != null; )
|
||||
{
|
||||
if ("runelite.version".equals(line))
|
||||
{
|
||||
doAll = true;
|
||||
}
|
||||
else if (line.startsWith("plugins/"))
|
||||
{
|
||||
buildList.add(new File(line));
|
||||
}
|
||||
else if (line.startsWith("package/") || line.startsWith("templateplugin/") || line.startsWith("create_new_plugin.py"))
|
||||
{
|
||||
doPackageTests = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doPackageTests)
|
||||
{
|
||||
new ProcessBuilder(new File(PACKAGE_ROOT, "gradlew").getAbsolutePath(), "--console=plain", "test")
|
||||
.directory(PACKAGE_ROOT)
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor();
|
||||
}
|
||||
|
||||
if (doAll)
|
||||
{
|
||||
isBuildingAll = true;
|
||||
buildList = listAllPlugins();
|
||||
}
|
||||
|
||||
gitdiff.waitFor(1, TimeUnit.SECONDS);
|
||||
if (gitdiff.exitValue() != 0)
|
||||
{
|
||||
throw new RuntimeException("git diff exited with " + gitdiff.exitValue());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("missing env vars");
|
||||
}
|
||||
|
||||
Packager pkg = new Packager(buildList);
|
||||
pkg.getUploadConfig().fromEnvironment(pkg.getRuneliteVersion());
|
||||
pkg.setAlwaysPrintLog(!pkg.getUploadConfig().isComplete());
|
||||
pkg.setIgnoreOldManifest(isBuildingAll);
|
||||
pkg.buildPlugins();
|
||||
}
|
||||
|
||||
static List<File> listAllPlugins()
|
||||
{
|
||||
return Arrays.asList(PLUGIN_ROOT.listFiles());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.MoreFiles;
|
||||
import com.google.common.io.RecursiveDeleteOption;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.imageio.ImageIO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import okhttp3.HttpUrl;
|
||||
import org.gradle.tooling.CancellationTokenSource;
|
||||
import org.gradle.tooling.GradleConnectionException;
|
||||
import org.gradle.tooling.GradleConnector;
|
||||
import org.gradle.tooling.ProjectConnection;
|
||||
import org.gradle.tooling.ResultHandler;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.slf4j.helpers.FormattingTuple;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
public class Plugin implements Closeable
|
||||
{
|
||||
private static final Pattern PLUGIN_INTERNAL_NAME_TEST = Pattern.compile("^[a-z0-9-]+$");
|
||||
private static final Pattern REPOSITORY_TEST = Pattern.compile("^https://github\\.com/.*\\.git$");
|
||||
private static final Pattern COMMIT_TEST = Pattern.compile("^[a-fA-F0-9]{40}$");
|
||||
|
||||
private static final File TMP_ROOT;
|
||||
private static final File GRADLE_HOME;
|
||||
|
||||
static
|
||||
{
|
||||
ImageIO.setUseCache(false);
|
||||
|
||||
try
|
||||
{
|
||||
TMP_ROOT = Files.createTempDirectory("pluginhub-package").toFile();
|
||||
TMP_ROOT.deleteOnExit();
|
||||
|
||||
GRADLE_HOME = new File(com.google.common.io.Files.asCharSource(new File(Packager.PACKAGE_ROOT, "build/gradleHome"), StandardCharsets.UTF_8).read().trim());
|
||||
if (!GRADLE_HOME.exists())
|
||||
{
|
||||
throw new RuntimeException("gradle home has moved");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final String internalName;
|
||||
|
||||
private final File buildDirectory;
|
||||
|
||||
@VisibleForTesting
|
||||
final File repositoryDirectory;
|
||||
|
||||
private final File jarFile;
|
||||
private final File iconFile;
|
||||
|
||||
@Getter
|
||||
private final File logFile;
|
||||
|
||||
@Getter
|
||||
private FileOutputStream log;
|
||||
|
||||
@Nullable
|
||||
private final String warning;
|
||||
|
||||
private final String repositoryURL;
|
||||
private final String commit;
|
||||
|
||||
@Getter
|
||||
private final ExternalPluginManifest manifest = new ExternalPluginManifest();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private long buildTimeMS;
|
||||
|
||||
public Plugin(File pluginCommitDescriptor) throws IOException, DisabledPluginException, PluginBuildException
|
||||
{
|
||||
internalName = pluginCommitDescriptor.getName();
|
||||
if (!PLUGIN_INTERNAL_NAME_TEST.matcher(internalName).matches())
|
||||
{
|
||||
throw PluginBuildException.of(internalName, "invalid plugin file name \"{}\"", internalName)
|
||||
.withHelp("plugin file names must be lowercase alphanumeric + dashes. try: \""
|
||||
+ internalName.toLowerCase().replaceAll("[^a-z0-9]+", "-") + "\"")
|
||||
.withFile(pluginCommitDescriptor);
|
||||
}
|
||||
|
||||
Properties cd = loadProperties(pluginCommitDescriptor);
|
||||
|
||||
String disabled = cd.getProperty("disabled");
|
||||
if (!Strings.isNullOrEmpty(disabled))
|
||||
{
|
||||
throw new DisabledPluginException(internalName, disabled);
|
||||
}
|
||||
|
||||
repositoryURL = (String) cd.remove("repository");
|
||||
if (repositoryURL == null)
|
||||
{
|
||||
throw PluginBuildException.of(internalName, "repository is missing from {}", pluginCommitDescriptor)
|
||||
.withFile(pluginCommitDescriptor);
|
||||
}
|
||||
|
||||
if (!REPOSITORY_TEST.matcher(repositoryURL).matches())
|
||||
{
|
||||
throw PluginBuildException.of(internalName, "repository is not an accepted url")
|
||||
.withFileLine(pluginCommitDescriptor, "repository=" + repositoryURL)
|
||||
.withHelp(() ->
|
||||
{
|
||||
if (!repositoryURL.startsWith("https"))
|
||||
{
|
||||
return "repositories must be https clone urls, not git:";
|
||||
}
|
||||
if (!repositoryURL.contains("github"))
|
||||
{
|
||||
return "repositories must be hosted on GitHub.com";
|
||||
}
|
||||
if (!repositoryURL.endsWith(".git"))
|
||||
{
|
||||
return "repository must be a clone url ~ it should end with .git";
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
commit = (String) cd.remove("commit");
|
||||
if (!COMMIT_TEST.matcher(commit).matches())
|
||||
{
|
||||
throw PluginBuildException.of(internalName, "commit must be a full 40 character sha1sum")
|
||||
.withFileLine(pluginCommitDescriptor, "commit=" + commit);
|
||||
}
|
||||
|
||||
warning = (String) cd.remove("warning");
|
||||
|
||||
for (Map.Entry<Object, Object> extra : cd.entrySet())
|
||||
{
|
||||
throw PluginBuildException.of(internalName, "unexpected key in commit descriptor")
|
||||
.withFileLine(pluginCommitDescriptor, extra.getKey() + "=" + extra.getValue());
|
||||
}
|
||||
|
||||
buildDirectory = new File(TMP_ROOT, internalName);
|
||||
if (!buildDirectory.mkdirs())
|
||||
{
|
||||
throw new RuntimeException("Unable to create temp directory");
|
||||
}
|
||||
repositoryDirectory = new File(buildDirectory, "repo");
|
||||
logFile = new File(buildDirectory, "log");
|
||||
log = new FileOutputStream(logFile, true);
|
||||
jarFile = new File(buildDirectory, "plugin.jar");
|
||||
iconFile = new File(repositoryDirectory, "icon.png");
|
||||
}
|
||||
|
||||
public void download() throws IOException, PluginBuildException
|
||||
{
|
||||
Process gitclone = new ProcessBuilder("git", "clone", "--config", "advice.detachedHead=false", this.repositoryURL, repositoryDirectory.getAbsolutePath())
|
||||
.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile))
|
||||
.redirectError(ProcessBuilder.Redirect.appendTo(logFile))
|
||||
.start();
|
||||
Util.waitAndCheck(this, gitclone, "git clone", 2, TimeUnit.MINUTES);
|
||||
|
||||
|
||||
Process gitcheckout = new ProcessBuilder("git", "checkout", commit + "^{commit}")
|
||||
.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile))
|
||||
.redirectError(ProcessBuilder.Redirect.appendTo(logFile))
|
||||
.directory(repositoryDirectory)
|
||||
.start();
|
||||
Util.waitAndCheck(this, gitcheckout, "git checkout", 2, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public void build(String runeliteVersion) throws IOException, PluginBuildException
|
||||
{
|
||||
try (ProjectConnection con = GradleConnector.newConnector()
|
||||
.forProjectDirectory(repositoryDirectory)
|
||||
.useInstallation(GRADLE_HOME)
|
||||
.connect())
|
||||
{
|
||||
CancellationTokenSource cancel = GradleConnector.newCancellationTokenSource();
|
||||
BlockingQueue<Object> queue = new ArrayBlockingQueue<>(1);
|
||||
String buildSuccess = "success";
|
||||
|
||||
con.newBuild()
|
||||
.withArguments(
|
||||
"--no-build-cache",
|
||||
"--console=plain",
|
||||
"--init-script", new File("./package/target_init.gradle").getAbsolutePath())
|
||||
.setEnvironmentVariables(ImmutableMap.of(
|
||||
"runelite.pluginhub.package.lib", new File(Packager.PACKAGE_ROOT, "initLib/build/libs/initLib.jar").toString(),
|
||||
"runelite.pluginhub.package.buildDir", buildDirectory.getAbsolutePath(),
|
||||
"runelite.pluginhub.package.runeliteVersion", runeliteVersion))
|
||||
.setJvmArguments("-Xmx768M", "-XX:+UseParallelGC")
|
||||
.setStandardOutput(log)
|
||||
.setStandardError(log)
|
||||
.forTasks("runelitePluginHubPackage", "runelitePluginHubManifest")
|
||||
.withCancellationToken(cancel.token())
|
||||
.run(new ResultHandler<Void>()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Void result)
|
||||
{
|
||||
queue.add(buildSuccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(GradleConnectionException failure)
|
||||
{
|
||||
queue.add(failure);
|
||||
}
|
||||
});
|
||||
log.flush();
|
||||
|
||||
Object output = queue.poll(5, TimeUnit.MINUTES);
|
||||
if (output == null)
|
||||
{
|
||||
cancel.cancel();
|
||||
throw PluginBuildException.of(this, "build did not complete within 5 minutes");
|
||||
}
|
||||
if (output == buildSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (output instanceof GradleConnectionException)
|
||||
{
|
||||
throw PluginBuildException.of(this, "build failed", output);
|
||||
}
|
||||
throw new IllegalStateException(output.toString());
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void assembleManifest() throws IOException, PluginBuildException
|
||||
{
|
||||
manifest.setInternalName(internalName);
|
||||
manifest.setCommit(commit);
|
||||
manifest.setWarning(warning);
|
||||
|
||||
{
|
||||
Properties chunk = loadProperties(new File(buildDirectory, "chunk.properties"));
|
||||
|
||||
manifest.setVersion(chunk.getProperty("version"));
|
||||
if (Strings.isNullOrEmpty(manifest.getVersion()))
|
||||
{
|
||||
throw new IllegalStateException("version in empty");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
long size = jarFile.length();
|
||||
if (size > 10 * 1024 * 1024)
|
||||
{
|
||||
throw PluginBuildException.of(this, "the output jar is {}MiB, which is above our limit of 10MiB", size / (1024 * 1024));
|
||||
}
|
||||
manifest.setSize((int) size);
|
||||
}
|
||||
|
||||
manifest.setHash(com.google.common.io.Files.asByteSource(jarFile)
|
||||
.hash(Hashing.sha256())
|
||||
.toString());
|
||||
|
||||
if (iconFile.exists())
|
||||
{
|
||||
long size = iconFile.length();
|
||||
if (size > 256 * 1024)
|
||||
{
|
||||
throw PluginBuildException.of(this, "icon.png is {}KiB, which is above our limit of 256KiB", size / 1024)
|
||||
.withFile(iconFile);
|
||||
}
|
||||
|
||||
synchronized (ImageIO.class)
|
||||
{
|
||||
try
|
||||
{
|
||||
Objects.requireNonNull(ImageIO.read(iconFile));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw PluginBuildException.of(this, "icon is invalid", e)
|
||||
.withFile(iconFile);
|
||||
}
|
||||
}
|
||||
|
||||
manifest.setHasIcon(true);
|
||||
}
|
||||
|
||||
Set<String> pluginClasses = new HashSet<>();
|
||||
Set<String> jarClasses = new HashSet<>();
|
||||
{
|
||||
try (JarInputStream jis = new JarInputStream(new FileInputStream(jarFile)))
|
||||
{
|
||||
for (JarEntry je; (je = jis.getNextJarEntry()) != null; )
|
||||
{
|
||||
String fileName = je.getName();
|
||||
if (!fileName.endsWith(".class"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] classData = ByteStreams.toByteArray(jis);
|
||||
new ClassReader(classData).accept(new ClassVisitor(Opcodes.ASM7)
|
||||
{
|
||||
boolean extendsPlugin;
|
||||
String name;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
||||
{
|
||||
if (version > Opcodes.V1_8 && !fileName.startsWith("META-INF/versions"))
|
||||
{
|
||||
throw PluginBuildException.of(Plugin.this, "plugins must be Java 1.8 compatible")
|
||||
.withFile(fileName);
|
||||
}
|
||||
|
||||
jarClasses.add(name.replace('/', '.'));
|
||||
|
||||
extendsPlugin = "net/runelite/client/plugins/Plugin".equals(superName);
|
||||
this.name = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible)
|
||||
{
|
||||
if ("Lnet/runelite/client/plugins/PluginDescriptor;".equals(descriptor) && extendsPlugin)
|
||||
{
|
||||
pluginClasses.add(name.replace('/', '.'));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
File propFile = new File(repositoryDirectory, "runelite-plugin.properties");
|
||||
if (!propFile.exists())
|
||||
{
|
||||
throw PluginBuildException.of(this, "runelite-plugin.properties must exist in the root of your repo");
|
||||
}
|
||||
Properties props = loadProperties(propFile);
|
||||
|
||||
{
|
||||
String displayName = (String) props.remove("displayName");
|
||||
if (Strings.isNullOrEmpty(displayName))
|
||||
{
|
||||
throw PluginBuildException.of(this, "\"displayName\" must be set")
|
||||
.withFile(propFile);
|
||||
}
|
||||
manifest.setDisplayName(displayName);
|
||||
}
|
||||
|
||||
{
|
||||
String author = (String) props.remove("author");
|
||||
if (Strings.isNullOrEmpty(author))
|
||||
{
|
||||
throw PluginBuildException.of(this, "\"author\" must be set")
|
||||
.withFile(propFile);
|
||||
}
|
||||
manifest.setAuthor(author);
|
||||
}
|
||||
|
||||
{
|
||||
String supportStr = (String) props.remove("support");
|
||||
if (!Strings.isNullOrEmpty(supportStr))
|
||||
{
|
||||
try
|
||||
{
|
||||
manifest.setSupport(new URL(supportStr));
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
throw PluginBuildException.of(this, "support url is malformed", e)
|
||||
.withFileLine(propFile, "support=" + supportStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest.setDescription((String) props.remove("description"));
|
||||
|
||||
{
|
||||
String tagsStr = (String) props.remove("tags");
|
||||
if (!Strings.isNullOrEmpty(tagsStr))
|
||||
{
|
||||
manifest.setTags(Splitter.on(",")
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.splitToList(tagsStr)
|
||||
.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
String pluginsStr = (String) props.remove("plugins");
|
||||
if (pluginsStr == null)
|
||||
{
|
||||
throw PluginBuildException.of(this, "\"plugins\" must be set")
|
||||
.withFile(propFile);
|
||||
}
|
||||
|
||||
List<String> plugins = Splitter.on(CharMatcher.anyOf(",:;"))
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.splitToList(pluginsStr);
|
||||
|
||||
manifest.setPlugins(plugins.toArray(new String[0]));
|
||||
|
||||
for (String className : plugins)
|
||||
{
|
||||
if (pluginClasses.contains(className))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (jarClasses.contains(className))
|
||||
{
|
||||
throw PluginBuildException.of(this, "Plugin class \"{}\" is not a valid Plugin", className)
|
||||
.withHelp("All plugins must extend Plugin an have an @PluginDescriptor")
|
||||
.withFileLine(propFile, "plugins=" + pluginsStr);
|
||||
}
|
||||
|
||||
Set<String> unusedPlugins = new HashSet<>(pluginClasses);
|
||||
unusedPlugins.removeAll(plugins);
|
||||
|
||||
throw PluginBuildException.of(this,
|
||||
"Plugin class \"{}\" is missing from the output jar", className)
|
||||
.withHelp(unusedPlugins.isEmpty()
|
||||
? "All plugins must extend Plugin an have an @PluginDescriptor"
|
||||
: ("Perhaps you wanted " + String.join(", ", unusedPlugins)))
|
||||
.withFileLine(propFile, "plugins=" + pluginsStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (props.size() != 0)
|
||||
{
|
||||
writeLog("warning: unused props in runelite-plugin.properties: {}\n", props.keySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void upload(UploadConfiguration uploadConfig) throws IOException
|
||||
{
|
||||
HttpUrl pluginRoot = uploadConfig.getUploadRepoRoot().newBuilder()
|
||||
.addPathSegment(internalName)
|
||||
.build();
|
||||
|
||||
uploadConfig.put(
|
||||
pluginRoot.newBuilder().addPathSegment(commit + ".jar").build(),
|
||||
jarFile);
|
||||
|
||||
if (manifest.isHasIcon())
|
||||
{
|
||||
uploadConfig.put(
|
||||
pluginRoot.newBuilder().addPathSegment(commit + ".png").build(),
|
||||
iconFile);
|
||||
}
|
||||
}
|
||||
|
||||
public void uploadLog(UploadConfiguration uploadConfig) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
log.close();
|
||||
log = null;
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
|
||||
uploadConfig.put(uploadConfig.getUploadRepoRoot()
|
||||
.newBuilder()
|
||||
.addPathSegment(internalName)
|
||||
.addPathSegment(commit + ".log")
|
||||
.build(),
|
||||
logFile);
|
||||
}
|
||||
|
||||
public void writeLog(String format, Object... args) throws IOException
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(format, args);
|
||||
log.write(fmt.getMessage().getBytes(StandardCharsets.UTF_8));
|
||||
Throwable t = fmt.getThrowable();
|
||||
if (t != null)
|
||||
{
|
||||
PrintWriter pw = new PrintWriter(new OutputStreamWriter(log, StandardCharsets.UTF_8));
|
||||
pw.println(t.getMessage());
|
||||
t.printStackTrace(pw);
|
||||
pw.flush();
|
||||
}
|
||||
log.flush();
|
||||
}
|
||||
|
||||
static Properties loadProperties(File path) throws IOException
|
||||
{
|
||||
Properties props = new Properties();
|
||||
try (FileInputStream fis = new FileInputStream(path))
|
||||
{
|
||||
props.load(fis);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
if (log != null)
|
||||
{
|
||||
log.close();
|
||||
}
|
||||
MoreFiles.deleteRecursively(buildDirectory.toPath(), RecursiveDeleteOption.ALLOW_INSECURE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.helpers.FormattingTuple;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
public class PluginBuildException extends Exception
|
||||
{
|
||||
private String file;
|
||||
private String line;
|
||||
private String help;
|
||||
|
||||
private PluginBuildException(String message, Throwable throwable)
|
||||
{
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public static PluginBuildException of(String internalName, String message, Object... args)
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(internalName + ": " + message, args);
|
||||
return new PluginBuildException(fmt.getMessage(), fmt.getThrowable());
|
||||
}
|
||||
|
||||
public static PluginBuildException of(Plugin plugin, String message, Object... args)
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(plugin.getInternalName() + ": " + message, args);
|
||||
return new PluginBuildException(fmt.getMessage(), fmt.getThrowable());
|
||||
}
|
||||
|
||||
public PluginBuildException withFileLine(File file, String line)
|
||||
{
|
||||
return withFileLine(file.toString(), line);
|
||||
}
|
||||
|
||||
public PluginBuildException withFile(File file)
|
||||
{
|
||||
return withFileLine(file, null);
|
||||
}
|
||||
|
||||
public PluginBuildException withFileLine(String file, String line)
|
||||
{
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginBuildException withFile(String file)
|
||||
{
|
||||
return withFileLine(file, null);
|
||||
}
|
||||
|
||||
public PluginBuildException withHelp(String help)
|
||||
{
|
||||
this.help = help;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginBuildException withHelp(Supplier<String> help)
|
||||
{
|
||||
this.help = help.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getHelpText()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (this.file != null)
|
||||
{
|
||||
sb.append("in file ").append(this.file);
|
||||
if (this.line != null)
|
||||
{
|
||||
sb.append(":\n").append(line);
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
if (this.help != null)
|
||||
{
|
||||
sb.append(help);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s)
|
||||
{
|
||||
super.printStackTrace(s);
|
||||
s.println("\n");
|
||||
s.println(getMessage());
|
||||
s.println(getHelpText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter s)
|
||||
{
|
||||
super.printStackTrace(s);
|
||||
s.println("\n");
|
||||
s.println(getMessage());
|
||||
s.println(getHelpText());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Base64;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
public class UploadConfiguration
|
||||
{
|
||||
private RSAPrivateCrtKey key;
|
||||
private PublicKey cert;
|
||||
private OkHttpClient client;
|
||||
|
||||
@Setter
|
||||
private HttpUrl uploadRepoRoot;
|
||||
|
||||
public UploadConfiguration fromEnvironment(String runeliteVersion)
|
||||
{
|
||||
String prNo = System.getenv("PACKAGE_IS_PR");
|
||||
if (prNo != null && !prNo.isEmpty() && !"false".equalsIgnoreCase(prNo))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
setKey(System.getenv("SIGNING_KEY"));
|
||||
setClient(System.getenv("REPO_CREDS"));
|
||||
|
||||
String uploadRepoRootStr = System.getenv("REPO_ROOT");
|
||||
if (!Strings.isNullOrEmpty(uploadRepoRootStr))
|
||||
{
|
||||
uploadRepoRoot = HttpUrl.parse(uploadRepoRootStr)
|
||||
.newBuilder()
|
||||
.addPathSegment(runeliteVersion)
|
||||
.build();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isComplete()
|
||||
{
|
||||
return key != null && cert != null && client != null && uploadRepoRoot != null;
|
||||
}
|
||||
|
||||
public UploadConfiguration setKey(String keyStr)
|
||||
{
|
||||
if (keyStr == null)
|
||||
{
|
||||
key = null;
|
||||
cert = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
|
||||
byte[] pkcs8 = Base64.getMimeDecoder().decode(keyStr
|
||||
.replace("\\n", "\n")
|
||||
.replaceAll(" |-----(BEGIN|END) PRIVATE KEY-----(\n?)", ""));
|
||||
key = (RSAPrivateCrtKey) kf.generatePrivate(new PKCS8EncodedKeySpec(pkcs8));
|
||||
cert = kf.generatePublic(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
|
||||
}
|
||||
catch (NoSuchAlgorithmException | InvalidKeySpecException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public UploadConfiguration setClient(String credentials)
|
||||
{
|
||||
String repoAuth = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
|
||||
client = new OkHttpClient.Builder()
|
||||
.addInterceptor(chain ->
|
||||
{
|
||||
Request userAgentRequest = chain.request()
|
||||
.newBuilder()
|
||||
.header("User-Agent", "RuneLite-PluginHub-Package/2")
|
||||
.header("Authorization", repoAuth)
|
||||
.build();
|
||||
|
||||
Response res = null;
|
||||
for (int attempts = 0; attempts < 2; attempts++)
|
||||
{
|
||||
res = chain.proceed(userAgentRequest);
|
||||
if (res.code() == 520)
|
||||
{
|
||||
res.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
})
|
||||
.build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void put(HttpUrl path, File data) throws IOException
|
||||
{
|
||||
try (Response res = client.newCall(new Request.Builder()
|
||||
.url(path)
|
||||
.put(RequestBody.create(null, data))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Util.check(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Util
|
||||
{
|
||||
private Util()
|
||||
{
|
||||
}
|
||||
|
||||
public static void waitAndCheck(Plugin plugin, Process process, String name, long timeout, TimeUnit timeoutUnit) throws PluginBuildException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!process.waitFor(timeout, timeoutUnit))
|
||||
{
|
||||
process.destroy();
|
||||
throw PluginBuildException.of(plugin, name + " failed to complete in a reasonable time");
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (process.exitValue() != 0)
|
||||
{
|
||||
throw PluginBuildException.of(plugin, name + " exited with " + process.exitValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static void check(Response res) throws IOException
|
||||
{
|
||||
if ((res.code() / 100) != 2)
|
||||
{
|
||||
throw new IOException(res.request().url() + ": " + res.code() + " " + res.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@Slf4j
|
||||
public class PluginTest
|
||||
{
|
||||
@Test
|
||||
public void testInternalNameChecks() throws IOException, DisabledPluginException
|
||||
{
|
||||
try
|
||||
{
|
||||
new Plugin(new File("plugins/I Like Spaces_and_UNDERSCORES"));
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "try: \"i-like-spaces-and-underscores\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitMustBeComplete() throws DisabledPluginException, IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
newPlugin("test", "" +
|
||||
"repository=https://github.com/runelite/example-plugin.git\n" +
|
||||
"commit=2357276b");
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "commit");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExamplePluginCompiles() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("example"))
|
||||
{
|
||||
p.build(Packager.readRLVersion());
|
||||
p.assembleManifest();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingPlugin() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("missing-plugin"))
|
||||
{
|
||||
File propFile = new File(p.repositoryDirectory, "runelite-plugin.properties");
|
||||
Properties props = Plugin.loadProperties(propFile);
|
||||
props.setProperty("plugins", "com.nonexistent");
|
||||
writeProperties(props, propFile);
|
||||
p.build(Packager.readRLVersion());
|
||||
p.assembleManifest();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "com.example.ExamplePlugin");
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeProperties(Properties props, File fi) throws IOException
|
||||
{
|
||||
try (FileOutputStream fos = new FileOutputStream(fi))
|
||||
{
|
||||
props.store(fos, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static Plugin newPlugin(String name, String desc) throws DisabledPluginException, PluginBuildException, IOException
|
||||
{
|
||||
File tmp = Files.createTempDir();
|
||||
File f = new File(tmp, name);
|
||||
try
|
||||
{
|
||||
Files.asCharSink(f, StandardCharsets.UTF_8).write(desc);
|
||||
return new Plugin(f);
|
||||
}
|
||||
finally
|
||||
{
|
||||
f.delete();
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static Plugin createExamplePlugin(String name) throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
Plugin p = newPlugin(name, "" +
|
||||
"repository=https://github.com/runelite/example-plugin.git\n" +
|
||||
"commit=0000000000000000000000000000000000000000");
|
||||
|
||||
Assert.assertEquals(new ProcessBuilder(
|
||||
new File("./create_new_plugin.py").getAbsolutePath(),
|
||||
"--noninteractive",
|
||||
"--output_directory", p.repositoryDirectory.getAbsolutePath(),
|
||||
"--name", "Example",
|
||||
"--package", "com.example",
|
||||
"--author", "Nobody",
|
||||
"--description", "An example greeter plugin")
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor(), 0);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void assertContains(String haystack, String needle)
|
||||
{
|
||||
Assert.assertTrue(haystack, haystack.contains(needle));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.pluginhub.packager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UploadConfigurationTest
|
||||
{
|
||||
public static final String TEST_SIGNING_KEY = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDw78Jgex/z/Wqp\n" +
|
||||
"CHLYYOQvD2yfOog2UJO7dZ8USRjDOUY6TVjqMg1aHNeI5USKG3jNKchw513PAfqO\n" +
|
||||
"j36S3I3CEv30kUBB/bgirG0YV/vJtuTcfiAa4Hl3JIKCDRi5gLkgMON8TYZs/afA\n" +
|
||||
"LVkR8ZFLlTlxLGE0VAReKXZH69poRdQLcAhRybvWWPKywWFXU4yfZzTIkQs82PXN\n" +
|
||||
"UyClxsOtEGYLlZ50oov6hx+YMPJaXdVOA1Ly01iBkB7gVQnCb/dCJqilNZab+5ja\n" +
|
||||
"xep4MPLBa39+dpGDECB8al2siYMEFWRT8eY8RAknQa6tL6ubUccHZEmuFT+n+II0\n" +
|
||||
"Zl6UPg/bAgMBAAECggEAaTD0l3UKJVd96uDSa2AaH+XHEdnXQId7iHu5AX1Mf2eR\n" +
|
||||
"HsFIUa+anr465/zZKMcHveNBLPIGxetiPj2uEGaUyafLEq0b9fPVIeZQFzHKr23X\n" +
|
||||
"i+DRGYrp3TemdytKoSrvKHvPxiR+zTUNuVzTJ39lZS94jc3HfrYz1fyaNJpnl+AT\n" +
|
||||
"6neUBfjXLUJYGzlLVouFIhsVywsF5Hk7N2UnSmQRdrFvgGE0IRUMqicXFuv6agxf\n" +
|
||||
"NI3Bdqqnzgfel26v7OKocbuolS2Zcr4hyPJdTtrNs5pf9tt2fIkV4P6sTJiVUSyw\n" +
|
||||
"TEHiCyZZyYDi4qr6F2yNkl/Ew6jNHcUA+XuTDE4OCQKBgQD79AnLckE9xIm1W+mN\n" +
|
||||
"4qjsZQ5Lxt7wQdXZ9Lovo+VUDJ7/X0qnND0n62OIO1yLW8PgOEzu3xb6f3sBGTp2\n" +
|
||||
"Yt+4QQmVRga5qHZ58pu3/P3YXM+C95X+x6GcIwisFH+8KEOFoFfGq3jbOUbF0uXt\n" +
|
||||
"LpuaDiEMR95+96gyTTc9qmTy1wKBgQD0zmxhRgWmhJqFTb2LLvY7E6G7BuVDys4A\n" +
|
||||
"lDrwNMklcw5LKabR539LbSEXx06fwQMlDyeY0NaRlx2DEFdgTH4CW7Nq/baHjuWl\n" +
|
||||
"Hq+4PJZvvBC9Ti9yWmDw0lcCJm4y0Kv78yIIwmtG+4LIru6l1/02+iChtYGEA540\n" +
|
||||
"801mgRGunQKBgQCYRU0GH+8+HWH8safdkHb3J7wUIATsv103dKhx0mPvABG31SeR\n" +
|
||||
"Fgk/7wsgcn/j2XnwMRaN51ZD3nfAmjazBd6fxO69wKyf2CiCWxWxhL0F3lGrnWaR\n" +
|
||||
"rKUHcET1ew4X8V2djOJ/t3I7S8pyFJvRVLHF0XQ3r9fQdGy6ueAA7NJF0QKBgBwQ\n" +
|
||||
"YfpQxasOPoyTmewPySiCmqLPKo83+5+zXoJU+s4xP208bCRaDoy+CPIp5giIXuzr\n" +
|
||||
"rNVm84IjOb3hrLKcckGg85OLXFZz+j2QpAJR58kNXTnmcagBVmWlJ1ZWw4FNzLmI\n" +
|
||||
"aNlqOFQd1yNccn1Oonef+weuwBc7NvLJBZF/sGA9AoGACDOQErTZC2HmiqgLGwqs\n" +
|
||||
"cg1MKU6a5gIpo7/zhR8beU4zKZfRMqmASI5KCA1JEGEnlJHyvGUwQcAx5Eu/JVXo\n" +
|
||||
"ckOxZ50guxkBMUHvEi6EIOKRsCqVbgVM6/HEYMj5z8VVn32vN1VYFk7Ng461RSgL\n" +
|
||||
"PTrpFFdp8oDxvgezLBFzqd8=\n" +
|
||||
"-----END PRIVATE KEY-----";
|
||||
|
||||
@Test
|
||||
public void createClientWithCredentials() throws IOException, InterruptedException
|
||||
{
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
server.enqueue(new MockResponse().setResponseCode(520).setBody("some cloudflare html"));
|
||||
server.enqueue(new MockResponse().setResponseCode(200).setBody("ok"));
|
||||
|
||||
OkHttpClient client = new UploadConfiguration()
|
||||
.setClient("Aladdin:open sesame")
|
||||
.getClient();
|
||||
|
||||
try (Response res = client.newCall(new Request.Builder()
|
||||
.put(RequestBody.create(null, "foo"))
|
||||
.url(server.url("/"))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Assert.assertEquals(res.code(), 200);
|
||||
Assert.assertEquals(res.body().string(), "ok");
|
||||
}
|
||||
|
||||
RecordedRequest r2 = server.takeRequest();
|
||||
Assert.assertEquals(r2.getHeader("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSigning() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException
|
||||
{
|
||||
UploadConfiguration cfg = new UploadConfiguration()
|
||||
.setKey(TEST_SIGNING_KEY);
|
||||
|
||||
byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] sig;
|
||||
{
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initSign(cfg.getKey());
|
||||
s.update(data);
|
||||
sig = s.sign();
|
||||
}
|
||||
|
||||
{
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initVerify(cfg.getCert());
|
||||
s.update(data);
|
||||
Assert.assertTrue(s.verify(sig));
|
||||
}
|
||||
|
||||
{
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initVerify(cfg.getCert());
|
||||
s.update("moo".getBytes(StandardCharsets.UTF_8));
|
||||
Assert.assertFalse(s.verify(sig));
|
||||
}
|
||||
}
|
||||
}
|
||||
4
package/settings.gradle
Normal file
4
package/settings.gradle
Normal file
@@ -0,0 +1,4 @@
|
||||
rootProject.name = "package-root"
|
||||
|
||||
include "initLib"
|
||||
include "package"
|
||||
81
package/target_init.gradle
Normal file
81
package/target_init.gradle
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
initscript {
|
||||
dependencies {
|
||||
classpath files(System.getenv("runelite.pluginhub.package.lib"))
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: "java"
|
||||
apply plugin: ShadowPlugin
|
||||
|
||||
compileJava {
|
||||
options.release.set(8)
|
||||
}
|
||||
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
|
||||
def buildDir = new File(System.getenv("runelite.pluginhub.package.buildDir"));
|
||||
|
||||
task runelitePluginHubPackage(type: ShadowJar) {
|
||||
destinationDir = buildDir
|
||||
archiveName = "plugin.jar"
|
||||
configurations = [project.configurations.runtimeClasspath]
|
||||
from sourceSets.main.output
|
||||
}
|
||||
|
||||
task runelitePluginHubManifest {
|
||||
doLast {
|
||||
def props = new Properties()
|
||||
props["version"] = project.version
|
||||
new File(buildDir, "chunk.properties").withOutputStream {
|
||||
props.store(it, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task configured {
|
||||
def runeLiteDeps = [
|
||||
"client",
|
||||
"runelite-api",
|
||||
"http-api",
|
||||
]
|
||||
def version = System.getenv("runelite.pluginhub.package.runeliteVersion")
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == "net.runelite" && details.requested.name in runeLiteDeps) {
|
||||
details.useVersion version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2019 Abex
|
||||
# Copyright (c) 2020 Abex
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@@ -23,23 +23,16 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[[ "${TRAVIS_PULL_REQUEST:-false}" == "false" ]] || exit 0
|
||||
set -e -x
|
||||
|
||||
SECONDS=0
|
||||
: '
|
||||
env:
|
||||
-FORCE_BUILD: example-external-plugin
|
||||
'
|
||||
pushd "$(dirname "$0")"
|
||||
./gradlew --console=plain --build-cache prep
|
||||
popd
|
||||
|
||||
for PLUGIN in plugins/* ; do
|
||||
if [ $SECONDS -gt 60 ]; then
|
||||
echo "travis_fold:start:intermediate_manifest"
|
||||
./build_manifest.sh
|
||||
SECONDS=0
|
||||
echo "travis_fold:end:intermediate_manifest"
|
||||
fi
|
||||
PLUGIN_ID=$(basename "$PLUGIN")
|
||||
echo "travis_fold:start:$PLUGIN_ID"
|
||||
./build_plugin.sh "$PLUGIN" || echo "Build failed for $PLUGIN_ID"
|
||||
echo "travis_fold:end:$PLUGIN_ID"
|
||||
done
|
||||
|
||||
echo "travis_fold:start:final_manifest"
|
||||
./build_manifest.sh
|
||||
echo "travis_fold:end:final_manifest"
|
||||
PACKAGE_IS_PR="$TRAVIS_PULL_REQUEST" \
|
||||
PACKAGE_COMMIT_RANGE="$TRAVIS_COMMIT_RANGE" \
|
||||
java -XX:+UseParallelGC -jar package/package/build/libs/package.jar
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# export REPO_CREDS="user:password"
|
||||
# export REPO_ROOT="https://your/webdav/server"
|
||||
# export SIGNING_KEY="
|
||||
# -----BEGIN PRIVATE KEY-----
|
||||
# ...
|
||||
# -----END PRIVATE KEY-----
|
||||
# "
|
||||
56
travis.sh
56
travis.sh
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2019 Abex
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set -e -x
|
||||
|
||||
: '
|
||||
env:
|
||||
-FORCE_BUILD: example-external-plugin
|
||||
'
|
||||
|
||||
if [[ "$FORCE_BUILD" == "ALL" ]]; then
|
||||
./rebuild_all.sh
|
||||
exit
|
||||
elif [[ -n "${FORCE_BUILD+x}" ]]; then
|
||||
for FI in $(echo "$FORCE_BUILD" | tr ',' '\n'); do
|
||||
./build_plugin.sh "plugins/$FI"
|
||||
done
|
||||
./build_manifest.sh
|
||||
exit
|
||||
fi
|
||||
|
||||
PLUGIN_CHANGE=
|
||||
while read -r FI ; do
|
||||
if [[ $FI =~ ^plugins/.*$ ]]; then
|
||||
[ -e "$FI" ] && ./build_plugin.sh "$FI" < /dev/null
|
||||
PLUGIN_CHANGE=true
|
||||
elif [[ "$FI" == "runelite.version" ]]; then
|
||||
./rebuild_all.sh < /dev/null
|
||||
fi
|
||||
done < <(git diff --name-only "$TRAVIS_COMMIT_RANGE")
|
||||
|
||||
if [[ "$PLUGIN_CHANGE" == true ]]; then
|
||||
./build_manifest.sh
|
||||
fi
|
||||
Reference in New Issue
Block a user