diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 93c06f1e..3e17d475 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,7 @@ on: - adopt11 - adopt13 - adopt14 + - adopt15 tags: - "[0-9]+.[0-9]+.[0-9]+" - "[0-9]+.[0-9]+.[0-9]+-openj9" @@ -16,6 +17,7 @@ on: - "[0-9]+.[0-9]+.[0-9]+-adopt11" - "[0-9]+.[0-9]+.[0-9]+-adopt13" - "[0-9]+.[0-9]+.[0-9]+-adopt14" + - "[0-9]+.[0-9]+.[0-9]+-adopt15" jobs: test: diff --git a/README.md b/README.md index 0aa245fa..5340d3ae 100644 --- a/README.md +++ b/README.md @@ -301,13 +301,17 @@ or downloading a world with the `WORLD` option. There are two additional volumes that can be mounted; `/mods` and `/config`. Any files in either of these filesystems will be copied over to the main -`/data` filesystem before starting Minecraft. If you want old mods to be removed as the `/mods` content is updated, then add `-e REMOVE_OLD_MODS=TRUE`. +`/data` filesystem before starting Minecraft. If you want old mods to be removed as the `/mods` content is updated, then add `-e REMOVE_OLD_MODS=TRUE`. If you are running a `BUKKIT` distribution this will affect all files inside the `plugins/` directory. You can fine tune the removal process by specifing the `REMOVE_OLD_MODS_INCLUDE` and `REMOVE_OLD_MODS_EXCLUDE` variables. By default everything will be removed. You can also specify the `REMOVE_OLD_MODS_DEPTH` (default 16) variable to only delete files up to a certain level. + +> For example: `-e REMOVE_OLD_MODS=TRUE -e REMOVE_OLD_MODS_INCLUDE="*.jar" -e REMOVE_OLD_MODS_DEPTH=1` will remove all old jar files that are directly inside the `plugins/` or `mods/` directory. This works well if you want to have a common set of modules in a separate location, but still have multiple worlds with different server requirements in either persistent volumes or a downloadable archive. +You can specify the destination of the configs that are located inside the `/config` mount by setting the `COPY_CONFIG_DEST` variable. The configs are copied recursivly to the `/data/config` directory by default. If a file was updated directly inside the `/data/*` directoy and is newer than the file in the `/config/*` mount it will not be overriden. +> For example: `-v ./config:/config -e COPY_CONFIG_DEST=/data` will allow you to copy over your `bukkit.yml` and so on directly into the server directory. ### Replacing variables inside configs diff --git a/docker-versions-create.sh b/docker-versions-create.sh index b9a4c0a9..5c745bca 100755 --- a/docker-versions-create.sh +++ b/docker-versions-create.sh @@ -1,7 +1,7 @@ #!/bin/bash #set -x # Use this variable to indicate a list of branches that docker hub is watching -branches_list=('openj9' 'openj9-nightly' 'adopt11' 'adopt13' 'adopt14' 'multiarch' 'multiarch-latest') +branches_list=('openj9' 'openj9-nightly' 'adopt11' 'adopt13' 'adopt14' 'adopt15' 'multiarch' 'multiarch-latest') function TrapExit { echo "Checking out back in master" diff --git a/start-finalSetupModpack b/start-finalSetupModpack index e21340cc..7cfabefc 100644 --- a/start-finalSetupModpack +++ b/start-finalSetupModpack @@ -1,19 +1,26 @@ #!/bin/bash -set -e +set -e -o pipefail . ${SCRIPTS:-/}start-utils +if isDebugging; then + set -x +fi # CURSE_URL_BASE used in manifest downloads below CURSE_URL_BASE=${CURSE_URL_BASE:-https://minecraft.curseforge.com/projects} # Remove old mods/plugins -if [ "$REMOVE_OLD_MODS" = "TRUE" ]; then - if [ "$TYPE" = "SPIGOT" ]; then - rm -rf /data/plugins/* - else - rm -rf /data/mods/* - fi +if isTrue ${REMOVE_OLD_MODS}; then + remove_mods_dest="/data/mods" + case ${TYPE} in + SPIGOT|BUKKIT|PAPER) + remove_mods_dest="/data/plugins" + ;; + esac + + log "Removing old mods in $remove_mods_dest..." + find $remove_mods_dest -mindepth 1 -maxdepth ${REMOVE_OLD_MODS_DEPTH:-16} -wholename "${REMOVE_OLD_MODS_INCLUDE:-*}" -not -wholename "${REMOVE_OLD_MODS_EXCLUDE}" -delete fi # If supplied with a URL for a modpack (simple zip of jars), download it and unpack @@ -22,7 +29,7 @@ if [[ "$MODPACK" ]]; then if [[ "${MODPACK}" == *.zip ]]; then downloadUrl="${MODPACK}" else - downloadUrl=$(curl -Ls -o /dev/null -w %{url_effective} $MODPACK) + downloadUrl=$(curl -Ls -o /dev/null -w %{effective_url} $MODPACK) if ! [[ $downloadUrl == *.zip ]]; then log "ERROR Invalid URL given for MODPACK: $downloadUrl resolved from $MODPACK" log " Must be HTTP or HTTPS and a ZIP file" @@ -58,39 +65,31 @@ fi # If supplied with a URL for a plugin download it. if [[ "$MODS" ]]; then + if [ "$TYPE" = "SPIGOT" ]; then + out_dir=/data/plugins + else + out_dir=/data/mods + fi + mkdir -p "$out_dir" + for i in ${MODS//,/ } do if isURL $i; then - if [[ $i == *.jar ]]; then - EFFECTIVE_MOD_URL=$i - else - EFFECTIVE_MOD_URL=$(curl -Ls -o /dev/null -w %{url_effective} $i) - if ! [[ $EFFECTIVE_MOD_URL == *.jar ]]; then - log "ERROR Invalid URL given in MODS: $EFFECTIVE_MOD_URL resolved from $i" - log " Must be HTTP or HTTPS and a JAR file" - exit 1 + log "Downloading mod/plugin $i ..." + effective_url=$(resolveEffectiveUrl "$i") + if isValidFileURL jar "${effective_url}"; then + out_file=$(getFilenameFromUrl "${effective_url}") + if ! curl -fsSL -o "${out_dir}/$out_file" "${effective_url}"; then + log "ERROR: failed to download from $i into $out_dir" + exit 2 fi - fi - - log "Downloading mod/plugin via HTTP" - log " from $EFFECTIVE_MOD_URL ..." - if ! curl -sSL -o /tmp/${EFFECTIVE_MOD_URL##*/} $EFFECTIVE_MOD_URL; then - log "ERROR: failed to download from $EFFECTIVE_MOD_URL to /tmp/${EFFECTIVE_MOD_URL##*/}" + else + log "ERROR: $effective_url resolved from $i is not a valid jar URL" exit 2 fi - - if [ "$TYPE" = "SPIGOT" ]; then - mkdir -p /data/plugins - mv /tmp/${EFFECTIVE_MOD_URL##*/} /data/plugins/${EFFECTIVE_MOD_URL##*/} - else - mkdir -p /data/mods - mv /tmp/${EFFECTIVE_MOD_URL##*/} /data/mods/${EFFECTIVE_MOD_URL##*/} - fi - rm -f /tmp/${EFFECTIVE_MOD_URL##*/} - else log "ERROR Invalid URL given in MODS: $i" - exit 1 + exit 2 fi done fi @@ -100,7 +99,7 @@ if [[ "$MANIFEST" ]]; then EFFECTIVE_MANIFEST_FILE=$MANIFEST elif isURL "$MANIFEST"; then EFFECTIVE_MANIFEST_FILE=/tmp/manifest.json - EFFECTIVE_MANIFEST_URL=$(curl -Ls -o /dev/null -w %{url_effective} $MANIFEST) + EFFECTIVE_MANIFEST_URL=$(curl -Ls -o /dev/null -w %{effective_url} $MANIFEST) curl -Ls -o $EFFECTIVE_MANIFEST_FILE "$EFFECTIVE_MANIFEST_URL" else log "MANIFEST='$MANIFEST' is not a valid manifest url or location" @@ -121,7 +120,7 @@ case "X$EFFECTIVE_MANIFEST_FILE" in do if [ ! -f $MOD_DIR/${p}_${f}.jar ] then - redirect_url="$(curl -Ls -o /dev/null -w %{url_effective} ${CURSE_URL_BASE}/${p})" + redirect_url="$(curl -Ls -o /dev/null -w %{effective_url} ${CURSE_URL_BASE}/${p})" url="$redirect_url/download/${f}/file" log Downloading curseforge mod $url # Manifest usually doesn't have mod names. Using id should be fine, tho @@ -140,10 +139,9 @@ fi if [[ "${GENERIC_PACK}" ]]; then if isURL "${GENERIC_PACK}"; then - generic_pack_url=${GENERIC_PACK} - GENERIC_PACK=/tmp/$(basename ${generic_pack_url}) - log "Downloading generic pack from ${generic_pack_url} ..." - curl -fsSL -o ${GENERIC_PACK} ${generic_pack_url} + log "Downloading generic pack ..." + curl -fsSL -o /tmp/generic_pack.zip "${GENERIC_PACK}" + GENERIC_PACK=/tmp/generic_pack.zip fi sum_file=/data/.generic_pack.sum diff --git a/start-finalSetupWorld b/start-finalSetupWorld index 24642e59..5d1bd79c 100644 --- a/start-finalSetupWorld +++ b/start-finalSetupWorld @@ -25,7 +25,12 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); mkdir -p /tmp/world-data (cd /tmp/world-data && unzip -o -q "$zipSrc") - baseDirs=$(find /tmp/world-data -name "level.dat" -exec dirname "{}" \;) + if [ "$TYPE" = "SPIGOT" ]; then + baseDirs=$(find /tmp/world-data -name "level.dat" -not -path "*_nether*" -not -path "*_the_end*" -exec dirname "{}" \;) + else + baseDirs=$(find /tmp/world-data -name "level.dat" -exec dirname "{}" \;) + fi + count=$(echo "$baseDirs" | wc -l) if [[ $count -gt 1 ]]; then baseDir="$(echo "$baseDirs" | sed -n ${WORLD_INDEX:-1}p)" @@ -38,6 +43,11 @@ if [[ "$WORLD" ]] && ( isTrue "${FORCE_WORLD_COPY}" || [ ! -d "$worldDest" ] ); exit 1 fi rsync --remove-source-files --recursive --delete "$baseDir/" "$worldDest" + if [ "$TYPE" = "SPIGOT" ]; then + log "Copying end and nether ..." + [ -d "${baseDir}_nether" ] && rsync --remove-source-files --recursive --delete "${baseDir}_nether/" "${worldDest}_nether" + [ -d "${baseDir}_the_end" ] && rsync --remove-source-files --recursive --delete "${baseDir}_the_end/" "${worldDest}_the_end" + fi else log "Cloning world directory from $WORLD ..." rsync --recursive --delete "${WORLD%/}"/ "$worldDest" diff --git a/start-minecraftFinalSetup b/start-minecraftFinalSetup index 14410555..2a59c7f4 100644 --- a/start-minecraftFinalSetup +++ b/start-minecraftFinalSetup @@ -2,6 +2,8 @@ . ${SCRIPTS:-/}start-utils +: ${COPY_CONFIG_DEST:="/data/config"} + if [ -n "$OPS" ]; then log "Setting/adding ops" rm -rf /data/ops.txt.converted @@ -54,20 +56,14 @@ done if [ -d /mods ]; then log "Copying any mods over..." mkdir -p /data/mods - if isTrue "${REMOVE_OLD_MODS}"; then - rsyncArgs=(--delete) - fi rsync -a --out-format="update:%f:Last Modified %M" "${rsyncArgs[@]}" --prune-empty-dirs --update /mods /data fi -[ -d /data/config ] || mkdir /data/config -for c in /config/* -do - if [ -f "$c" ]; then - log Copying configuration $(basename "$c") - cp -rf "$c" /data/config - fi -done +if [ -d /config ]; then + log "Copying any configs from /config to $COPY_CONFIG_DEST" + mkdir -p $COPY_CONFIG_DEST + rsync -a --out-format="update:%f:Last Modified %M" "${rsyncArgs[@]}" --prune-empty-dirs --update /config/ $COPY_CONFIG_DEST +fi EXTRA_ARGS="" # Optional disable console diff --git a/start-utils b/start-utils index a46d1a28..b461a836 100644 --- a/start-utils +++ b/start-utils @@ -1,8 +1,14 @@ #!/bin/bash -function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } +function join_by() { + local d=$1 + shift + echo -n "$1" + shift + printf "%s" "${@/#/$d}" +} -function isURL { +function isURL() { local value=$1 if [[ ${value:0:8} == "https://" || ${value:0:7} == "http://" ]]; then @@ -12,90 +18,114 @@ function isURL { fi } -function isTrue { +function isValidFileURL() { + suffix=${1:?Missing required suffix arg} + url=${2:?Missing required url arg} + + [[ "$url" == http*://*.${suffix} || "$url" == http*://*.${suffix}\?* ]] +} + +function resolveEffectiveUrl() { + url="${1:?Missing required url argument}" + if ! curl -Ls -o /dev/null -w %{url_effective} "$url"; then + log "ERROR failed to resolve effective URL from $url" + exit 2 + fi +} + +function getFilenameFromUrl() { + url="${1:?Missing required url argument}" + strippedOfQuery="${url%\?*}" + basename "$strippedOfQuery" +} + +function isTrue() { local value=${1,,} result= case ${value} in - true|on) - result=0 - ;; - *) - result=1 - ;; + true | on) + result=0 + ;; + *) + result=1 + ;; esac return ${result} } -function isDebugging { - if [[ -v DEBUG ]] && [[ ${DEBUG^^} = TRUE ]]; then +function isDebugging() { + if [[ -v DEBUG ]] && [[ ${DEBUG^^} == TRUE ]]; then return 0 else return 1 fi } -function debug { +function debug() { if isDebugging; then log "DEBUG: $*" fi } -function logn { +function logn() { echo -n "[init] $*" } -function log { +function log() { echo "[init] $*" } -function logAutopause { +function logAutopause() { echo "[Autopause loop] $*" } -function logAutopauseAction { +function logAutopauseAction() { echo "[$(date -Iseconds)] [Autopause] $*" } -function normalizeMemSize { +function normalizeMemSize() { local scale=1 case ${1,,} in - *k) - scale=1024;; - *m) - scale=1048576;; - *g) - scale=1073741824;; + *k) + scale=1024 + ;; + *m) + scale=1048576 + ;; + *g) + scale=1073741824 + ;; esac - val=${1:0: -1} - echo $(( val * scale )) + val=${1:0:-1} + echo $((val * scale)) } -function versionLessThan { +function versionLessThan() { local activeParts - IFS=. read -ra activeParts <<< "${VANILLA_VERSION}" + IFS=. read -ra activeParts <<<"${VANILLA_VERSION}" local givenParts - IFS=. read -ra givenParts <<< "$1" + IFS=. read -ra givenParts <<<"$1" - if (( ${#activeParts[@]} < 2 )); then + if ((${#activeParts[@]} < 2)); then return 1 fi - if (( ${#activeParts[@]} == 2 )); then - if (( activeParts[0] < givenParts[0] )) || \ - (( activeParts[0] == givenParts[0] && activeParts[1] < givenParts[1] )); then + if ((${#activeParts[@]} == 2)); then + if ((activeParts[0] < givenParts[0])) || + ((activeParts[0] == givenParts[0] && activeParts[1] < givenParts[1])); then return 0 else return 1 fi else - if (( activeParts[0] < givenParts[0] )) || \ - (( activeParts[0] == givenParts[0] && activeParts[1] < givenParts[1] )) || \ - (( activeParts[0] == givenParts[0] && activeParts[1] == givenParts[1] && activeParts[2] < givenParts[2] )); then + if ((activeParts[0] < givenParts[0])) || + ((activeParts[0] == givenParts[0] && activeParts[1] < givenParts[1])) || + ((activeParts[0] == givenParts[0] && activeParts[1] == givenParts[1] && activeParts[2] < givenParts[2])); then return 0 else return 1 @@ -115,10 +145,10 @@ requireVar() { } function writeEula() { -if ! echo "# Generated via Docker on $(date) + if ! echo "# Generated via Docker on $(date) eula=${EULA,,} -" > /data/eula.txt; then - log "ERROR: unable to write eula to /data. Please make sure attached directory is writable by uid=${UID}" - exit 2 -fi +" >/data/eula.txt; then + log "ERROR: unable to write eula to /data. Please make sure attached directory is writable by uid=${UID}" + exit 2 + fi }