3442 lines
125 KiB
Bash
3442 lines
125 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# release.sh generates an addon zip file from a Git, SVN, or Mercurial checkout.
|
|
#
|
|
# This is free and unencumbered software released into the public domain.
|
|
#
|
|
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
# distribute this software, either in source code form or as a compiled
|
|
# binary, for any purpose, commercial or non-commercial, and by any
|
|
# means.
|
|
#
|
|
# In jurisdictions that recognize copyright laws, the author or authors
|
|
# of this software dedicate any and all copyright interest in the
|
|
# software to the public domain. We make this dedication for the benefit
|
|
# of the public at large and to the detriment of our heirs and
|
|
# successors. We intend this dedication to be an overt act of
|
|
# relinquishment in perpetuity of all present and future rights to this
|
|
# software under copyright law.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
# OTHER DEALINGS IN THE SOFTWARE.
|
|
#
|
|
# For more information, please refer to <http://unlicense.org/>
|
|
|
|
# Global shellcheck excludes:
|
|
# SC2295: Expansions inside ${..} need to be quoted separately, otherwise they will match as a pattern.
|
|
# SC2030: Modification of var is local (to subshell caused by pipeline).
|
|
# SC2031: var was modified in a subshell. That change might be lost.
|
|
# SC2317: Command appears to be unreachable.
|
|
# SC2329: This function is never invoked.
|
|
# SC1091: Not following, file does not exist.
|
|
# shellcheck disable=SC2295,SC2030,SC2031,SC2317,SC2329,SC1091
|
|
|
|
## USER OPTIONS
|
|
|
|
# Secrets for uploading
|
|
cf_token=
|
|
github_token=
|
|
wowi_token=
|
|
wago_token=
|
|
|
|
# Variables set via command-line options
|
|
slug=
|
|
addonid=
|
|
wagoid=
|
|
topdir=
|
|
releasedir=
|
|
overwrite=
|
|
nolib=
|
|
split=
|
|
line_ending="dos"
|
|
skip_copying=
|
|
skip_externals=
|
|
skip_localization=
|
|
skip_zipfile=
|
|
skip_upload=
|
|
skip_cf_upload=
|
|
pkgmeta_file=
|
|
game_version=
|
|
game_type=
|
|
file_type=
|
|
file_template="{package-name}-{project-version}{nolib}{classic}"
|
|
label_template="{project-version}{classic}{nolib}"
|
|
|
|
wowi_markup="bbcode"
|
|
|
|
## END USER OPTIONS
|
|
|
|
if [[ ${BASH_VERSINFO[0]} -lt 4 ]] || [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 3 ]]; then
|
|
echo "ERROR! bash version 4.3 or above is required. Your version is ${BASH_VERSION}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Game versions for uploading
|
|
declare -A game_flavor=( ["retail"]="retail" ["classic"]="classic" ["bcc"]="bcc" ["mainline"]="retail" ["tbc"]="bcc" ["vanilla"]="classic" ["wrath"]="wrath" ["wotlkc"]="wrath" ["cata"]="cata" ["mists"]="mists" ["titan"]="titan" ["forever"]="forever" ["camelot"]="forever" )
|
|
|
|
declare -A game_type_version=() # type -> version (: delim)
|
|
declare -A game_type_interface=() # type -> toc (: delim)
|
|
declare -A si_game_type_interface_all=() # type -> interface value (last file) (values from ## Interface*:)
|
|
declare -A si_game_type_interface=() # type -> game type interface value (last file) (values from ## Interface-Type:)
|
|
declare si_game_root_interface # base interface value (last file)
|
|
declare -A toc_interfaces=() # path -> si_game_type_interface_all (: delim)
|
|
declare -A toc_root_interface=() # path -> base interface value (values from ## Interface:)
|
|
declare -A toc_root_paths=() # path -> directory name
|
|
declare -a toc_paths=() # toc path in order of processing
|
|
|
|
# Script return code
|
|
exit_code=0
|
|
|
|
retry() {
|
|
local result=0
|
|
local count=1
|
|
local max=10
|
|
while [[ $count -le "$max" ]]; do
|
|
[[ $result -ne 0 ]] && {
|
|
echo -e "\033[01;31mRetrying (${count}/${max})\033[0m" >&2
|
|
}
|
|
"$@" && { result=0 && break; } || result="$?"
|
|
count="$((count + 1))"
|
|
sleep 3
|
|
done
|
|
return "$result"
|
|
}
|
|
|
|
retry_svn_checkout() {
|
|
local repo="$1"
|
|
local path="$2"
|
|
local result=0
|
|
local count=1
|
|
local max=5
|
|
while [[ $count -le "$max" ]]; do
|
|
[[ $result -ne 0 ]] && {
|
|
echo -e "\033[01;31mRetrying (${count}/${max})\033[0m" >&2
|
|
rm -rf "$path" # an aborted checkout can leave a .svn directory with a locked state
|
|
}
|
|
svn checkout -q "$repo" "$path" --force && { result=0 && break; } || result="$?"
|
|
count="$((count + 1))"
|
|
sleep 3
|
|
done
|
|
return "$result"
|
|
}
|
|
|
|
# Fetch a file from an svn repository. The file not existing is not an error.
|
|
svn_cat_optional() {
|
|
local url="$1"
|
|
local path="$2"
|
|
local error
|
|
if ! error=$( svn cat "$url" 2>&1 > "$path" ); then
|
|
rm -f "$path"
|
|
# 160013 is "path not found"
|
|
if [[ $error != *"160013"* ]]; then
|
|
echo "$error" >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Escape a string for use in sed substitutions.
|
|
escape_substr() {
|
|
local s="$1"
|
|
s=${s//\\/\\\\}
|
|
s=${s//\//\\/}
|
|
s=${s//&/\\&}
|
|
echo "$s"
|
|
}
|
|
|
|
# File name templating
|
|
filename_filter() {
|
|
local classic alpha beta invalid="_"
|
|
[ -n "$skip_invalid" ] && invalid="&"
|
|
if [[ -n $game_type ]] && [[ "$game_type" != "retail" ]] && \
|
|
[[ "$game_type" != "classic" || "${si_project_version,,}" != *"-classic"* ]] &&\
|
|
[[ "$game_type" != "bcc" || "${si_project_version,,}" != *"-bcc"* ]] &&\
|
|
[[ "$game_type" != "wrath" || "${si_project_version,,}" != *"-wrath"* ]] &&\
|
|
[[ "$game_type" != "cata" || "${si_project_version,,}" != *"-cata"* ]] &&\
|
|
[[ "$game_type" != "mists" || "${si_project_version,,}" != *"-mists"* ]] &&\
|
|
[[ "$game_type" != "titan" || "${si_project_version,,}" != *"-titan"* ]] &&\
|
|
[[ "$game_type" != "forever" || "${si_project_version,,}" != *"-forever"* ]]
|
|
then
|
|
# only append the game type if the tag doesn't include it
|
|
classic="-$game_type"
|
|
fi
|
|
[ "$file_type" == "alpha" ] && alpha="-alpha"
|
|
[ "$file_type" == "beta" ] && beta="-beta"
|
|
sed \
|
|
-e "s/{package-name}/$( escape_substr "$package" )/g" \
|
|
-e "s/{project-revision}/$si_project_revision/g" \
|
|
-e "s/{project-hash}/$si_project_hash/g" \
|
|
-e "s/{project-abbreviated-hash}/$si_project_abbreviated_hash/g" \
|
|
-e "s/{project-author}/$( escape_substr "$si_project_author" )/g" \
|
|
-e "s/{project-date-iso}/$si_project_date_iso/g" \
|
|
-e "s/{project-date-integer}/$si_project_date_integer/g" \
|
|
-e "s/{project-timestamp}/$si_project_timestamp/g" \
|
|
-e "s/{project-version}/$( escape_substr "$si_project_version" )/g" \
|
|
-e "s/{game-type}/${game_type}/g" \
|
|
-e "s/{release-type}/${file_type}/g" \
|
|
-e "s/{alpha}/${alpha}/g" \
|
|
-e "s/{beta}/${beta}/g" \
|
|
-e "s/{nolib}/${nolib:+-nolib}/g" \
|
|
-e "s/{classic}/${classic}/g" \
|
|
-e "s/\([^A-Za-z0-9._!-]\)/${invalid}/g" \
|
|
<<< "$1"
|
|
}
|
|
|
|
toc_filter() {
|
|
local keyword="$1"
|
|
local remove="$2"
|
|
if [ -z "$remove" ]; then
|
|
# "active" build type: remove comments (keep content), remove non-blocks (remove all)
|
|
sed \
|
|
-e "/#@\(end-\)\{0,1\}${keyword}@/d" \
|
|
-e "/#@non-${keyword}@/,/#@end-non-${keyword}@/d"
|
|
else
|
|
# "non" build type: remove blocks (remove content), uncomment non-blocks (remove tags)
|
|
sed \
|
|
-e "/#@${keyword}@/,/#@end-${keyword}@/d" \
|
|
-e "/#@non-${keyword}@/,/#@end-non-${keyword}@/s/^#[[:blank:]]\{1,\}//" \
|
|
-e "/#@\(end-\)\{0,1\}non-${keyword}@/d"
|
|
fi
|
|
}
|
|
|
|
toc_to_type() {
|
|
local toc_version="$1"
|
|
local -n game_type="$2" || return 1
|
|
case $toc_version in
|
|
11???) game_type="classic" ;;
|
|
16???) game_type="forever" ;;
|
|
20???) game_type="bcc" ;;
|
|
30???) game_type="wrath" ;;
|
|
40???) game_type="cata" ;;
|
|
50???) game_type="mists" ;;
|
|
380??) game_type="titan" ;;
|
|
*) game_type="retail"
|
|
esac
|
|
# return game_type
|
|
}
|
|
|
|
toc_to_file_type() {
|
|
local toc_version="$1"
|
|
local -n game_type="$2" || return 1
|
|
case $toc_version in
|
|
11???) game_type="classic" ;;
|
|
16???) game_type="forever" ;;
|
|
20???) game_type="bcc" ;;
|
|
30???) game_type="wrath" ;;
|
|
40???) game_type="cata" ;;
|
|
50???) game_type="mists" ;;
|
|
380??) game_type="wrath" ;;
|
|
*) game_type="retail"
|
|
esac
|
|
# return game_type
|
|
}
|
|
|
|
is_toc_multiple_types() {
|
|
local toc_version="$1"
|
|
local toc_game_type toc_version_game_type
|
|
IFS=':,' read -ra V <<< "$toc_version"
|
|
toc_to_type "${V[0]}" "toc_game_type"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_version_game_type"
|
|
[[ $toc_game_type != "$toc_version_game_type" ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
# Process command-line options
|
|
usage() {
|
|
cat <<-'EOF' >&2
|
|
Usage: release.sh [options]
|
|
-c Skip copying files into the package directory.
|
|
-d Skip uploading.
|
|
-e Skip checkout of external repositories.
|
|
-l Skip @localization@ keyword replacement.
|
|
-L Only do @localization@ keyword replacement (skip upload to CurseForge).
|
|
-o Keep existing package directory, overwriting its contents.
|
|
-s Create a stripped-down "nolib" package.
|
|
-S Create a package supporting multiple game types from a single TOC file.
|
|
-u Use Unix line-endings.
|
|
-z Skip zip file creation.
|
|
-t topdir Set top-level directory of checkout.
|
|
-r releasedir Set directory containing the package directory. Defaults to "$topdir/.release".
|
|
-p curse-id Set the project id used on CurseForge for localization and uploading. (Use 0 to unset the TOC value)
|
|
-w wowi-id Set the addon id used on WoWInterface for uploading. (Use 0 to unset the TOC value)
|
|
-a wago-id Set the project id used on Wago Addons for uploading. (Use 0 to unset the TOC value)
|
|
-g game-version Set the game version to use for uploading.
|
|
-m pkgmeta.yaml Set the pkgmeta file to use.
|
|
-n "{template}" Set the package zip file name and upload label. Use "-n help" for more info.
|
|
EOF
|
|
}
|
|
|
|
OPTIND=1
|
|
while getopts ":celLzusSop:dw:a:r:t:g:m:n:" opt; do
|
|
case $opt in
|
|
c) skip_copying="true" ;; # Skip copying files into the package directory
|
|
z) skip_zipfile="true" ;; # Skip creating a zip file
|
|
e) skip_externals="true" ;; # Skip checkout of external repositories
|
|
l) skip_localization="true" ;; # Skip @localization@ keyword replacement
|
|
L) skip_cf_upload="true" ;; # Skip uploading to CurseForge
|
|
d) skip_upload="true" ;; # Skip uploading
|
|
u) line_ending="unix" ;; # Use LF instead of CRLF as the line ending for all text files
|
|
o) overwrite="true" ;; # Don't delete existing directories in the release directory
|
|
p) slug="$OPTARG" ;; # Set CurseForge project id
|
|
w) addonid="$OPTARG" ;; # Set WoWInterface addon id
|
|
a) wagoid="$OPTARG" ;; # Set Wago Addons project id
|
|
r) releasedir="$OPTARG" ;; # Set the release directory
|
|
t) # Set the top-level directory of the checkout
|
|
if [ ! -d "$OPTARG" ]; then
|
|
echo "Invalid argument for option \"-t\" - Directory \"$OPTARG\" does not exist." >&2
|
|
usage
|
|
exit 1
|
|
fi
|
|
topdir="$OPTARG"
|
|
;;
|
|
s) # Create a nolib package without externals
|
|
nolib="true"
|
|
skip_externals="true"
|
|
;;
|
|
S) split="true" ;; # Split TOC
|
|
g) # Set the game type or version
|
|
OPTARG="${OPTARG,,}"
|
|
case "$OPTARG" in
|
|
retail|classic|bcc|wrath|cata|mists|titan|forever) game_type="$OPTARG" ;; # game_version from toc
|
|
mainline) game_type="retail" ;;
|
|
camelot) game_type="forever" ;;
|
|
*)
|
|
# Set game version (x.y.z)
|
|
# Build game type set from the last value if a list
|
|
IFS=',' read -ra V <<< "$OPTARG"
|
|
for i in "${V[@]}"; do
|
|
if [[ ! "$i" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)[a-z]?$ ]]; then
|
|
echo "Invalid argument for option \"-g\" ($i)" >&2
|
|
usage
|
|
exit 1
|
|
fi
|
|
if [[ ${BASH_REMATCH[1]} == "1" ]]; then
|
|
if [[ ${BASH_REMATCH[2]} == 6[0-9] ]]; then
|
|
game_type="forever"
|
|
else
|
|
game_type="classic"
|
|
fi
|
|
elif [[ ${BASH_REMATCH[1]} == "2" ]]; then
|
|
game_type="bcc"
|
|
elif [[ ${BASH_REMATCH[1]} == "3" ]]; then
|
|
if [[ ${BASH_REMATCH[2]} == "80" ]]; then
|
|
game_type="titan"
|
|
else
|
|
game_type="wrath"
|
|
fi
|
|
elif [[ ${BASH_REMATCH[1]} == "4" ]]; then
|
|
game_type="cata"
|
|
elif [[ ${BASH_REMATCH[1]} == "5" ]]; then
|
|
game_type="mists"
|
|
else
|
|
game_type="retail"
|
|
fi
|
|
if [[ -n ${game_type_version[$game_type]} ]]; then
|
|
game_type_version[$game_type]="${game_type_version[$game_type]}:$i"
|
|
game_type_interface[$game_type]="${game_type_interface[$game_type]}:$( printf "%d%02d%02d" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" )"
|
|
else
|
|
game_type_version[$game_type]="$i"
|
|
game_type_interface[$game_type]=$( printf "%d%02d%02d" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" )
|
|
fi
|
|
done
|
|
if [[ ${#game_type_version[@]} -gt 1 ]]; then
|
|
game_type=
|
|
fi
|
|
game_version="$OPTARG"
|
|
esac
|
|
;;
|
|
m) # Set the pkgmeta file
|
|
if [ ! -f "$OPTARG" ]; then
|
|
echo "Invalid argument for option \"-m\" - File \"$OPTARG\" does not exist." >&2
|
|
usage
|
|
exit 1
|
|
fi
|
|
pkgmeta_file="$OPTARG"
|
|
;;
|
|
n) # Set the package file name
|
|
if [ "$OPTARG" = "help" ]; then
|
|
cat <<-'EOF' >&2
|
|
Usage: release.sh [options]
|
|
Set the package zip file name and upload file label. There are several string
|
|
substitutions you can use to include version control and build type information in
|
|
the file name and upload label.
|
|
|
|
The default file name is "{package-name}-{project-version}{nolib}{classic}".
|
|
The default upload label is "{project-version}{classic}{nolib}".
|
|
|
|
To set both, separate with a ":", i.e, "{file template}:{label template}".
|
|
If either side of the ":" is blank, the default will be used.Not including a ":"
|
|
will set the file name template, leaving upload label as default.
|
|
|
|
Tokens: {package-name}{project-revision}{project-hash}{project-abbreviated-hash}
|
|
{project-author}{project-date-iso}{project-date-integer}{project-timestamp}
|
|
{project-version}{game-type}{release-type}
|
|
|
|
Flags: {alpha}{beta}{nolib}{classic}
|
|
|
|
Tokens are always replaced with their value. Flags are shown prefixed with a dash
|
|
depending on the build type.
|
|
EOF
|
|
exit 0
|
|
fi
|
|
if skip_invalid=true filename_filter "$OPTARG" | grep -q '[{}]'; then
|
|
tokens=$( skip_invalid=true filename_filter "$OPTARG" | sed -e '/^[^{]*{\|}[^{]*{\|}[^{]*/s//}{/g' -e 's/^}\({.*}\){$/\1/' )
|
|
echo "Invalid argument for option \"-n\" - Invalid substitutions: $tokens" >&2
|
|
exit 1
|
|
fi
|
|
file_template=${OPTARG%%:*}
|
|
if [ -z "$file_template" ]; then
|
|
file_template="{package-name}-{project-version}{nolib}{classic}"
|
|
fi
|
|
label_template=${OPTARG##*:}
|
|
if [ -z "$label_template" ]; then
|
|
label_template="{project-version}{classic}{nolib}"
|
|
fi
|
|
#"{package-name}-{project-version}{nolib}{classic}:{project-version}{classic}{nolib}"
|
|
;;
|
|
:)
|
|
echo "Option \"-$OPTARG\" requires an argument." >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
\?)
|
|
if [ "$OPTARG" = "?" ] || [ "$OPTARG" = "h" ]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
echo "Unknown option \"-$OPTARG\"" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
# Set $topdir to top-level directory of the checkout.
|
|
if [ -z "$topdir" ]; then
|
|
dir=$( pwd )
|
|
if [ -d "$dir/.git" ] || [ -d "$dir/.svn" ] || [ -d "$dir/.hg" ]; then
|
|
topdir=.
|
|
else
|
|
dir=${dir%/*}
|
|
topdir=".."
|
|
while [ -n "$dir" ]; do
|
|
if [ -d "$topdir/.git" ] || [ -d "$topdir/.svn" ] || [ -d "$topdir/.hg" ]; then
|
|
break
|
|
fi
|
|
dir=${dir%/*}
|
|
topdir="$topdir/.."
|
|
done
|
|
if [ ! -d "$topdir/.git" ] && [ ! -d "$topdir/.svn" ] && [ ! -d "$topdir/.hg" ]; then
|
|
echo "No Git, SVN, or Hg checkout found." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Handle folding sections in CI logs
|
|
start_group() { echo "$1"; }
|
|
end_group() { echo; }
|
|
|
|
# Check for Travis CI
|
|
if [ -n "$TRAVIS" ]; then
|
|
# Don't run the packager for pull requests
|
|
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
|
|
echo "Not packaging pull request."
|
|
exit 0
|
|
fi
|
|
if [ -z "$TRAVIS_TAG" ]; then
|
|
# Don't run the packager if there is a tag pending
|
|
check_tag=$( git -C "$topdir" tag --points-at HEAD )
|
|
if [ -n "$check_tag" ]; then
|
|
echo "Found future tag \"${check_tag}\", not packaging."
|
|
exit 0
|
|
fi
|
|
# Only package master, classic, or develop
|
|
if [ "$TRAVIS_BRANCH" != "master" ] && [ "$TRAVIS_BRANCH" != "classic" ] && [ "$TRAVIS_BRANCH" != "develop" ]; then
|
|
echo "Not packaging \"${TRAVIS_BRANCH}\"."
|
|
exit 0
|
|
fi
|
|
fi
|
|
# https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash
|
|
start_group() {
|
|
echo -en "travis_fold:start:$2\\r\033[0K"
|
|
# release_timer_id="$(printf %08x $((RANDOM * RANDOM)))"
|
|
# release_timer_start_time="$(date -u +%s%N)"
|
|
# echo -en "travis_time:start:${release_timer_id}\\r\033[0K"
|
|
echo "$1"
|
|
}
|
|
end_group() {
|
|
# local release_timer_end_time="$(date -u +%s%N)"
|
|
# local duration=$((release_timer_end_time - release_timer_start_time))
|
|
# echo -en "\\ntravis_time:end:${release_timer_id}:start=${release_timer_start_time},finish=${release_timer_end_time},duration=${duration}\\r\033[0K"
|
|
echo -en "travis_fold:end:$1\\r\033[0K"
|
|
}
|
|
fi
|
|
|
|
# Check for GitHub Actions
|
|
if [[ -n $GITHUB_ACTIONS ]]; then
|
|
# Prevent duplicate builds from multiple pushes
|
|
if [[ $GITHUB_EVENT_NAME == "push" && $GITHUB_REF == "refs/heads"* ]]; then
|
|
check_tag=$( git -C "$topdir" tag --points-at HEAD )
|
|
if [[ -n $check_tag ]]; then
|
|
echo "Found future tag \"${check_tag}\", not packaging."
|
|
exit 0
|
|
fi
|
|
unset check_tag
|
|
fi
|
|
start_group() { echo "##[group]$1"; }
|
|
end_group() { echo "##[endgroup]"; }
|
|
fi
|
|
|
|
# Load secrets
|
|
if [ -f "$topdir/.env" ]; then
|
|
. "$topdir/.env"
|
|
elif [ -f ".env" ]; then
|
|
. ".env"
|
|
fi
|
|
[ -z "$cf_token" ] && cf_token=$CF_API_TOKEN
|
|
[ -z "$cf_token" ] && cf_token=$CF_API_KEY # deprecated
|
|
[ -z "$github_token" ] && github_token=$GH_API_TOKEN
|
|
[ -z "$github_token" ] && github_token=$GITHUB_API_TOKEN
|
|
[ -z "$github_token" ] && github_token=$GITHUB_OAUTH # deprecated
|
|
[ -z "$wowi_token" ] && wowi_token=$WOWI_API_TOKEN
|
|
[ -z "$wago_token" ] && wago_token=$WAGO_API_TOKEN
|
|
|
|
# Set $releasedir to the directory which will contain the generated addon zipfile.
|
|
if [ -z "$releasedir" ]; then
|
|
releasedir="$topdir/.release"
|
|
fi
|
|
|
|
# Set $basedir to the basename of the checkout directory.
|
|
basedir=$( cd "$topdir" && pwd )
|
|
case $basedir in
|
|
/*/*) basedir=${basedir##/*/} ;;
|
|
/*) basedir=${basedir##/} ;;
|
|
esac
|
|
|
|
# Set $repository_type to "git" or "svn" or "hg".
|
|
repository_type=
|
|
if [ -d "$topdir/.git" ]; then
|
|
repository_type=git
|
|
elif [ -d "$topdir/.svn" ]; then
|
|
repository_type=svn
|
|
elif [ -d "$topdir/.hg" ]; then
|
|
repository_type=hg
|
|
else
|
|
echo "No Git, SVN, or Hg checkout found in \"$topdir\"." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# $releasedir must be an absolute path or inside $topdir.
|
|
case $releasedir in
|
|
/*) ;;
|
|
$topdir/*) ;;
|
|
*)
|
|
echo "The release directory \"$releasedir\" must be an absolute path or inside \"$topdir\"." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Create the staging directory.
|
|
mkdir -p "$releasedir" 2>/dev/null || {
|
|
echo "Unable to create the release directory \"$releasedir\"." >&2
|
|
exit 1
|
|
}
|
|
|
|
# Expand $topdir and $releasedir to their absolute paths for string comparisons later.
|
|
topdir=$( cd "$topdir" && pwd )
|
|
releasedir=$( cd "$releasedir" && pwd )
|
|
|
|
###
|
|
### set_info_<repo> returns the following information:
|
|
###
|
|
si_repo_type= # "git" or "svn" or "hg"
|
|
si_repo_dir= # the checkout directory
|
|
si_repo_url= # the checkout url
|
|
si_tag= # tag for HEAD
|
|
si_previous_tag= # previous tag
|
|
si_previous_revision= # [SVN|Hg] revision number for previous tag
|
|
|
|
si_project_revision= # Turns into the highest revision of the entire project in integer form, e.g. 1234, for SVN. Turns into the commit count for the project's hash for Git.
|
|
si_project_hash= # [Git|Hg] Turns into the hash of the entire project in hex form. e.g. 106c634df4b3dd4691bf24e148a23e9af35165ea
|
|
si_project_abbreviated_hash= # [Git|Hg] Turns into the abbreviated hash of the entire project in hex form. e.g. 106c63f
|
|
si_project_author= # Turns into the last author of the entire project. e.g. ckknight
|
|
si_project_date_iso= # Turns into the last changed date (by UTC) of the entire project in ISO 8601. e.g. 2008-05-01T12:34:56Z
|
|
si_project_date_integer= # Turns into the last changed date (by UTC) of the entire project in a readable integer fashion. e.g. 2008050123456
|
|
si_project_timestamp= # Turns into the last changed date (by UTC) of the entire project in POSIX timestamp. e.g. 1209663296
|
|
si_project_version= # Turns into an approximate version of the project. The tag name if on a tag, otherwise it's up to the repo. SVN returns something like "r1234", Git returns something like "v0.1-873fc1"
|
|
|
|
si_file_revision= # Turns into the current revision of the file in integer form, e.g. 1234, for SVN. Turns into the commit count for the file's hash for Git.
|
|
si_file_hash= # Turns into the hash of the file in hex form. e.g. 106c634df4b3dd4691bf24e148a23e9af35165ea
|
|
si_file_abbreviated_hash= # Turns into the abbreviated hash of the file in hex form. e.g. 106c63
|
|
si_file_author= # Turns into the last author of the file. e.g. ckknight
|
|
si_file_date_iso= # Turns into the last changed date (by UTC) of the file in ISO 8601. e.g. 2008-05-01T12:34:56Z
|
|
si_file_date_integer= # Turns into the last changed date (by UTC) of the file in a readable integer fashion. e.g. 20080501123456
|
|
si_file_timestamp= # Turns into the last changed date (by UTC) of the file in POSIX timestamp. e.g. 1209663296
|
|
|
|
si_build_timestamp=$( date "+%s" )
|
|
si_build_date=$( TZ='' printf "%(%Y-%m-%d)T" "$si_build_timestamp" )
|
|
si_build_date_iso=$( TZ='' printf "%(%Y-%m-%dT%H:%M:%SZ)T" "$si_build_timestamp" )
|
|
si_build_date_integer=$( TZ='' printf "%(%Y%m%d%H%M%S)T" "$si_build_timestamp" )
|
|
|
|
# SVN date helper function
|
|
strtotime() {
|
|
local value="$1" # datetime string
|
|
local format="$2" # strptime string
|
|
if [[ "$OSTYPE" == "darwin"* || "$OSTYPE" == *"bsd"* ]]; then # bsd
|
|
date -j -f "$format" "$value" "+%s" 2>/dev/null
|
|
else # gnu
|
|
date -d "$value" +%s 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
set_info_git() {
|
|
si_repo_dir="$1"
|
|
si_repo_type="git"
|
|
si_repo_url=$( git -C "$si_repo_dir" remote get-url origin 2>/dev/null | sed -e 's/^git@\(.*\):/https:\/\/\1\//' )
|
|
if [ -z "$si_repo_url" ]; then # no origin so grab the first fetch url
|
|
si_repo_url=$( git -C "$si_repo_dir" remote -v | awk '/(fetch)/ { print $2; exit }' | sed -e 's/^git@\(.*\):/https:\/\/\1\//' )
|
|
fi
|
|
|
|
# Populate filter vars.
|
|
si_project_hash=$( git -C "$si_repo_dir" show --no-patch --format="%H" 2>/dev/null )
|
|
si_project_abbreviated_hash=$( git -C "$si_repo_dir" show --no-patch --abbrev=7 --format="%h" 2>/dev/null )
|
|
si_project_author=$( git -C "$si_repo_dir" show --no-patch --format="%an" 2>/dev/null )
|
|
si_project_timestamp=$( git -C "$si_repo_dir" show --no-patch --format="%at" 2>/dev/null )
|
|
si_project_date_iso=$( TZ='' printf "%(%Y-%m-%dT%H:%M:%SZ)T" "$si_project_timestamp" )
|
|
si_project_date_integer=$( TZ='' printf "%(%Y%m%d%H%M%S)T" "$si_project_timestamp" )
|
|
# XXX --depth limits rev-list :\ [ -s "$si_repo_dir/$(git rev-parse --git-dir)/shallow" ] && git -C "$si_repo_dir" fetch --quiet --unshallow --no-tags
|
|
# actions/checkout using clone --filter=blob:none instead of their current unholy abomination would be nice
|
|
si_project_revision=$( git -C "$si_repo_dir" rev-list --count "$si_project_hash" 2>/dev/null )
|
|
|
|
# Get the tag for the HEAD.
|
|
si_previous_tag=
|
|
si_previous_revision=
|
|
# git describe should use the latest tag if there are multiple, but it doesn't always work that way.
|
|
local _si_tag check_tag=()
|
|
while IFS='' read -r tag; do check_tag+=( "$tag" ); done < <( git -C "$si_repo_dir" tag --points-at HEAD --sort=-committerdate --sort=-v:refname --sort=-taggerdate )
|
|
if [[ ${#check_tag[@]} -gt 0 ]]; then
|
|
# Use the most recent tag
|
|
si_tag="${check_tag[0]}"
|
|
_si_tag="$si_tag"
|
|
|
|
if [[ $GITHUB_REF == "refs/tags/"* && $si_tag != "$GITHUB_REF_NAME" ]]; then
|
|
# Git doesn't agree with GitHub Actions, check if there is another local tag that matches
|
|
for tag in "${check_tag[@]}"; do
|
|
if [[ $tag == "$GITHUB_REF_NAME" ]]; then
|
|
si_tag="$tag"
|
|
_si_tag="$si_tag"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
else
|
|
_si_tag=$( git -C "$si_repo_dir" describe --tags --always --abbrev=7 2>/dev/null )
|
|
si_tag=$( git -C "$si_repo_dir" describe --tags --always --abbrev=0 2>/dev/null )
|
|
fi
|
|
# Set $si_project_version to the version number of HEAD.
|
|
si_project_version="$si_tag"
|
|
# The HEAD is not tagged if the HEAD is several commits past the most recent tag.
|
|
if [[ $si_tag == "$si_project_hash" ]]; then
|
|
# --abbrev=0 expands out the full sha if there was no previous tag
|
|
si_project_version="$_si_tag"
|
|
si_previous_tag=
|
|
si_tag=
|
|
elif [[ $_si_tag != "$si_tag" ]]; then
|
|
# not on a tag
|
|
si_project_version=$( git -C "$si_repo_dir" describe --tags --abbrev=7 --exclude="*[Aa][Ll][Pp][Hh][Aa]*" 2>/dev/null )
|
|
if [[ -n $si_project_version ]]; then
|
|
si_previous_tag=$( git -C "$si_repo_dir" describe --tags --abbrev=0 --exclude="*[Aa][Ll][Pp][Hh][Aa]*" 2>/dev/null )
|
|
else # no previous non-alpha tag
|
|
si_project_version="$_si_tag"
|
|
si_previous_tag=
|
|
fi
|
|
si_tag=
|
|
else # we're on a tag, just jump back one commit
|
|
if [[ ${si_tag,,} != *"beta"* && ${si_tag,,} != *"alpha"* ]]; then
|
|
# full release, ignore beta tags
|
|
si_previous_tag=$( git -C "$si_repo_dir" describe --tags --abbrev=0 --exclude="*[Aa][Ll][Pp][Hh][Aa]*" --exclude="*[Bb][Ee][Tt][Aa]*" HEAD~ 2>/dev/null )
|
|
else
|
|
si_previous_tag=$( git -C "$si_repo_dir" describe --tags --abbrev=0 --exclude="*[Aa][Ll][Pp][Hh][Aa]*" HEAD~ 2>/dev/null )
|
|
fi
|
|
fi
|
|
}
|
|
|
|
set_info_svn() {
|
|
si_repo_dir="$1"
|
|
si_repo_type="svn"
|
|
|
|
local _si_svninfo _si_url _si_revision _si_tag_line _si_tag _si_tag_from_revision _si_timestamp
|
|
_si_svninfo=$( retry svn info -r BASE "$si_repo_dir" 2>/dev/null )
|
|
if [[ -z "$_si_svninfo" ]]; then
|
|
echo "Unable to read svn repo at $si_repo_dir" >&2
|
|
exit 1
|
|
fi
|
|
|
|
si_repo_url=$( awk '/^Repository Root:/ { print $3; exit }' <<< "$_si_svninfo" )
|
|
_si_url=$( awk '/^URL:/ { print $2; exit }' <<< "$_si_svninfo" )
|
|
_si_revision=$( awk '/^Last Changed Rev:/ { print $NF; exit }' <<< "$_si_svninfo" )
|
|
|
|
case ${_si_url#"${si_repo_url}"/} in
|
|
tags/*)
|
|
# Extract the tag from the URL.
|
|
si_tag=${_si_url#"${si_repo_url}"/tags/}
|
|
si_tag=${si_tag%%/*}
|
|
si_project_revision="$_si_revision"
|
|
;;
|
|
*)
|
|
# Check if the latest tag matches the working copy revision (/trunk checkout instead of /tags)
|
|
_si_tag_line=$( retry svn log --verbose --limit 1 "$si_repo_url/tags" 2>/dev/null | awk '/^ A/ { print $0; exit }' )
|
|
_si_tag=$( echo "$_si_tag_line" | awk '/^ A/ { print $2 }' | awk -F/ '{ print $NF }' )
|
|
# (from /project/trunk:REV)
|
|
_si_tag_from_revision=${_si_tag_line##*:}
|
|
_si_tag_from_revision=${_si_tag_from_revision%)*}
|
|
|
|
if [[ "$_si_tag_from_revision" == "$_si_revision" ]]; then
|
|
si_tag="$_si_tag"
|
|
si_project_revision=$( retry svn info "$si_repo_url/tags/$si_tag" 2>/dev/null | awk '/^Last Changed Rev:/ { print $NF; exit }' )
|
|
else
|
|
# Set $si_project_revision to the highest revision of the project at the checkout path
|
|
si_project_revision=$( svn info --recursive "$si_repo_dir" 2>/dev/null | awk '/^Last Changed Rev:/ { print $NF }' | sort -nr | head -n1 )
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [[ -n "$si_tag" ]]; then
|
|
si_project_version="$si_tag"
|
|
else
|
|
si_project_version="r$si_project_revision"
|
|
fi
|
|
|
|
# Get the previous tag and it's revision
|
|
_si_tag=$( retry svn log --verbose --limit 1 "$si_repo_url/tags" -r $((si_project_revision - 1)):1 2>/dev/null | awk '/^ A/ { print $0; exit }' | awk '/^ A/ { print $2 }' | awk -F/ '{ print $NF }' )
|
|
if [[ -n "$_si_tag" ]]; then
|
|
si_previous_tag="$_si_tag"
|
|
si_previous_revision=$( retry svn info "$si_repo_url/tags/$_si_tag" 2>/dev/null | awk '/^Last Changed Rev:/ { print $NF; exit }' )
|
|
fi
|
|
|
|
# Populate filter vars.
|
|
si_project_author=$( awk '/^Last Changed Author:/ { print $0; exit }' <<< "$_si_svninfo" | cut -d" " -f4- )
|
|
_si_timestamp=$( awk '/^Last Changed Date:/ { print $4,$5; exit }' <<< "$_si_svninfo" )
|
|
si_project_timestamp=$( strtotime "$_si_timestamp" "%F %T" )
|
|
si_project_date_iso=$( TZ='' printf "%(%Y-%m-%dT%H:%M:%SZ)T" "$si_project_timestamp" )
|
|
si_project_date_integer=$( TZ='' printf "%(%Y%m%d%H%M%S)T" "$si_project_timestamp" )
|
|
# SVN repositories have no project hash.
|
|
si_project_hash=
|
|
si_project_abbreviated_hash=
|
|
}
|
|
|
|
set_info_hg() {
|
|
si_repo_dir="$1"
|
|
si_repo_type="hg"
|
|
si_repo_url=$( hg --cwd "$si_repo_dir" paths -q default )
|
|
if [ -z "$si_repo_url" ]; then # no default so grab the first path
|
|
si_repo_url=$( hg --cwd "$si_repo_dir" paths | awk '{ print $3; exit }' )
|
|
fi
|
|
|
|
# Populate filter vars.
|
|
si_project_hash=$( hg --cwd "$si_repo_dir" log -r . --template '{node}' 2>/dev/null )
|
|
si_project_abbreviated_hash=$( hg --cwd "$si_repo_dir" log -r . --template '{node|short}' 2>/dev/null )
|
|
si_project_author=$( hg --cwd "$si_repo_dir" log -r . --template '{author}' 2>/dev/null )
|
|
si_project_timestamp=$( hg --cwd "$si_repo_dir" log -r . --template '{date}' 2>/dev/null | cut -d. -f1 )
|
|
si_project_date_iso=$( TZ='' printf "%(%Y-%m-%dT%H:%M:%SZ)T" "$si_project_timestamp" )
|
|
si_project_date_integer=$( TZ='' printf "%(%Y%m%d%H%M%S)T" "$si_project_timestamp" )
|
|
si_project_revision=$( hg --cwd "$si_repo_dir" log -r . --template '{rev}' 2>/dev/null )
|
|
|
|
# Get tag info
|
|
si_tag=
|
|
# I'm just muddling through revsets, so there is probably a better way to do this
|
|
# Ignore tag commits, so v1.0-1 will package as v1.0
|
|
if [ "$( hg --cwd "$si_repo_dir" log -r '.-filelog(.hgtags)' --template '{rev}' 2>/dev/null )" == "" ]; then
|
|
_si_tip=$( hg --cwd "$si_repo_dir" log -r 'last(parents(.))' --template '{rev}' 2>/dev/null )
|
|
else
|
|
_si_tip=$( hg --cwd "$si_repo_dir" log -r . --template '{rev}' 2>/dev/null )
|
|
fi
|
|
si_previous_tag=$( hg --cwd "$si_repo_dir" log -r "$_si_tip" --template '{latesttag}' 2>/dev/null )
|
|
# si_project_version=$( hg --cwd "$si_repo_dir" log -r "$_si_tip" --template "{ ifeq(changessincelatesttag, 0, latesttag, '{latesttag}-{changessincelatesttag}-m{node|short}') }" 2>/dev/null ) # git style
|
|
si_project_version=$( hg --cwd "$si_repo_dir" log -r "$_si_tip" --template "{ ifeq(changessincelatesttag, 0, latesttag, 'r{rev}') }" 2>/dev/null ) # svn style
|
|
if [ "$si_previous_tag" = "$si_project_version" ]; then
|
|
# we're on a tag
|
|
si_tag=$si_previous_tag
|
|
si_previous_tag=$( hg --cwd "$si_repo_dir" log -r "last(parents($_si_tip))" --template '{latesttag}' 2>/dev/null )
|
|
fi
|
|
si_previous_revision=$( hg --cwd "$si_repo_dir" log -r "$si_previous_tag" --template '{rev}' 2>/dev/null )
|
|
}
|
|
|
|
set_info_file() {
|
|
local _si_file="$1"
|
|
if [[ $si_repo_type == "git" ]]; then
|
|
local _si_file_dir=${_si_file%/*} # set the working directory to file's directory to allow for submodule file info
|
|
_si_file=${_si_file##*/} # just need the filename
|
|
# Populate filter vars from the last commit the file was included in.
|
|
si_file_author=$( git -C "$_si_file_dir" log --max-count=1 --format="%an" -- "$_si_file" 2>/dev/null )
|
|
si_file_timestamp=$( git -C "$_si_file_dir" log --max-count=1 --format="%at" -- "$_si_file" 2>/dev/null )
|
|
si_file_hash=$( git -C "$_si_file_dir" log --max-count=1 --format="%H" -- "$_si_file" 2>/dev/null )
|
|
si_file_revision=$( git -C "$_si_file_dir" rev-list --count "$si_file_hash" 2>/dev/null ) # XXX checkout depth affects rev-list, see set_info_git
|
|
si_file_abbreviated_hash=$( git -C "$_si_file_dir" log --max-count=1 --abbrev=7 --format="%h" -- "$_si_file" 2>/dev/null )
|
|
|
|
elif [[ $si_repo_type == "svn" ]]; then
|
|
local _sif_svninfo _si_timestamp
|
|
_sif_svninfo=$( svn info "$_si_file" 2>/dev/null )
|
|
if [[ -n $_sif_svninfo ]]; then
|
|
# Populate filter vars.
|
|
si_file_author=$( awk '/^Last Changed Author:/ { print $0; exit }' <<< "$_sif_svninfo" | cut -d" " -f4- )
|
|
_si_timestamp=$( awk '/^Last Changed Date:/ { print $4,$5,$6; exit }' <<< "$_sif_svninfo" )
|
|
si_file_timestamp=$( strtotime "$_si_timestamp" "%F %T %z" )
|
|
si_file_revision=$( awk '/^Last Changed Rev:/ { print $NF; exit }' <<< "$_sif_svninfo" )
|
|
# Use the file checksum.
|
|
si_file_hash=$( awk '/^Checksum:/ { print $2; exit }' <<< "$_sif_svninfo" )
|
|
si_file_abbreviated_hash=${si_file_hash:0:7}
|
|
else
|
|
si_file_author=
|
|
si_file_timestamp=
|
|
si_file_revision=
|
|
si_file_hash=
|
|
si_file_abbreviated_hash=
|
|
si_file_date_iso=
|
|
si_file_date_integer=
|
|
fi
|
|
|
|
elif [[ $si_repo_type == "hg" ]]; then
|
|
# Populate filter vars.
|
|
si_file_author=$( hg log --limit 1 --template '{author}' "$_si_file" 2>/dev/null )
|
|
si_file_timestamp=$( hg log --limit 1 --template '{date}' "$_si_file" 2>/dev/null | cut -d. -f1 )
|
|
si_file_revision=$( hg log --limit 1 --template '{rev}' "$_si_file" 2>/dev/null )
|
|
si_file_hash=$( hg log --limit 1 --template '{node}' "$_si_file" 2>/dev/null )
|
|
si_file_abbreviated_hash=$( hg log --limit 1 --template '{node|short}' "$_si_file" 2>/dev/null )
|
|
|
|
fi
|
|
if [[ -n $si_file_timestamp ]]; then
|
|
si_file_date_iso=$( TZ='' printf "%(%Y-%m-%dT%H:%M:%SZ)T" "$si_file_timestamp" )
|
|
si_file_date_integer=$( TZ='' printf "%(%Y%m%d%H%M%S)T" "$si_file_timestamp" )
|
|
fi
|
|
}
|
|
|
|
# Set some version info about the project
|
|
echo -ne "Reading project repository..."
|
|
case $repository_type in
|
|
git) set_info_git "$topdir" ;;
|
|
svn) set_info_svn "$topdir" ;;
|
|
hg) set_info_hg "$topdir" ;;
|
|
esac
|
|
echo -e "done\n"
|
|
|
|
tag=$si_tag
|
|
project_version=$si_project_version
|
|
previous_version=$si_previous_tag
|
|
project_hash=$si_project_hash
|
|
project_revision=$si_project_revision
|
|
previous_revision=$si_previous_revision
|
|
project_timestamp=$si_project_timestamp
|
|
project_github_url=
|
|
project_github_slug=
|
|
if [[ "$si_repo_url" == "https://github.com"* ]]; then
|
|
project_github_url=${si_repo_url%.git}
|
|
project_github_slug=${project_github_url#https://github.com/}
|
|
fi
|
|
project_site=
|
|
|
|
# Automatic file type detection based on CurseForge rules
|
|
# 1) Untagged commits will be marked as an alpha.
|
|
# 2) Tagged commits will be marked as a release with the following exceptions:
|
|
# - If the tag contains the word "alpha", it will be marked as an alpha file.
|
|
# - If instead the tag contains the word "beta", it will be marked as a beta file.
|
|
if [ -n "$tag" ]; then
|
|
if [[ "${tag,,}" == *"alpha"* ]]; then
|
|
file_type="alpha"
|
|
elif [[ "${tag,,}" == *"beta"* ]]; then
|
|
file_type="beta"
|
|
else
|
|
file_type="release"
|
|
fi
|
|
else
|
|
file_type="alpha"
|
|
fi
|
|
|
|
# Add some GitHub Actions outputs
|
|
[[ -n $GITHUB_ACTIONS ]] && {
|
|
echo "project_version=${project_version}"
|
|
echo "previous_version=${previous_version}"
|
|
echo "project_hash=${project_hash}"
|
|
echo "project_timestamp=${project_timestamp}"
|
|
echo "release_type=${file_type}"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
# Bare carriage-return character.
|
|
carriage_return=$( printf "\r" )
|
|
|
|
# Returns 0 if $1 matches one of the colon-separated patterns in $2.
|
|
match_pattern() {
|
|
local _mp_file="$1"
|
|
local _mp_list="$2:"
|
|
local _mp_pattern
|
|
while [ -n "$_mp_list" ]; do
|
|
_mp_pattern=${_mp_list%%:*}
|
|
_mp_list=${_mp_list#*:}
|
|
# shellcheck disable=SC2254 # _mp_pattern is a glob
|
|
case $_mp_file in
|
|
$_mp_pattern)
|
|
return 0
|
|
;;
|
|
esac
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
# Simple .pkgmeta YAML processor.
|
|
declare -A yaml_bool=( ["yes"]="yes" ["true"]="yes" ["on"]="yes" ["false"]="no" ["off"]="no" ["no"]="no" )
|
|
yaml_keyvalue() {
|
|
yaml_key=${1%%:*}
|
|
yaml_value=${1#"$yaml_key":}
|
|
yaml_value=${yaml_value#"${yaml_value%%[! ]*}"} # trim leading whitespace
|
|
if [[ -n "$yaml_value" && -n "${yaml_bool[${yaml_value,,}]}" ]]; then # normalize booleans
|
|
yaml_value="${yaml_bool[${yaml_value,,}]}"
|
|
fi
|
|
yaml_value=${yaml_value#[\'\"]} # trim leading quotes
|
|
yaml_value=${yaml_value%[\'\"]} # trim trailing quotes
|
|
}
|
|
|
|
yaml_listitem() {
|
|
yaml_item=${1#-}
|
|
yaml_item=${yaml_item#"${yaml_item%%[! ]*}"} # trim leading whitespace
|
|
yaml_item=${yaml_item#[\'\"]} # trim leading quotes
|
|
yaml_item=${yaml_item%[\'\"]} # trim trailing quotes
|
|
}
|
|
|
|
###
|
|
### Process .pkgmeta to set variables used later in the script.
|
|
###
|
|
|
|
if [ -z "$pkgmeta_file" ]; then
|
|
pkgmeta_file="$topdir/.pkgmeta"
|
|
# CurseForge allows this so check for it
|
|
if [ ! -f "$pkgmeta_file" ] && [ -f "$topdir/pkgmeta.yaml" ]; then
|
|
pkgmeta_file="$topdir/pkgmeta.yaml"
|
|
fi
|
|
fi
|
|
|
|
# Variables set via .pkgmeta.
|
|
package=
|
|
project=
|
|
manual_changelog=
|
|
changelog=
|
|
changelog_markup="text"
|
|
enable_nolib_creation=
|
|
ignore=
|
|
license=
|
|
unchanged=
|
|
zip_root_dirs=()
|
|
nolib_exclude=
|
|
wowi_gen_changelog="true"
|
|
wowi_archive="true"
|
|
wowi_convert_changelog="true"
|
|
declare -A relations=()
|
|
|
|
parse_ignore() {
|
|
pkgmeta="$1" # full path to .pkgmeta
|
|
copy_path="$2" # the post-copy base path to match
|
|
sub_path="$3" # only includes matches in this path
|
|
|
|
[ -f "$pkgmeta" ] || return 1
|
|
|
|
check_path="$topdir/" # paths are relative to the topdir
|
|
if [ -n "$copy_path" ]; then
|
|
check_path=$( dirname "$pkgmeta" )"/"
|
|
copy_path="$copy_path/"
|
|
fi
|
|
if [[ -n $sub_path && $sub_path != *"/" ]]; then
|
|
sub_path="$sub_path/"
|
|
fi
|
|
|
|
yaml_eof=
|
|
while [ -z "$yaml_eof" ]; do
|
|
IFS='' read -r yaml_line || yaml_eof="true"
|
|
# Skip commented out lines.
|
|
if [[ $yaml_line =~ ^[[:space:]]*\# ]]; then
|
|
continue
|
|
fi
|
|
# Strip any trailing CR character.
|
|
yaml_line="${yaml_line%"$carriage_return"}"
|
|
|
|
case $yaml_line in
|
|
[!\ ]*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
# Set the $pkgmeta_phase for stateful processing.
|
|
pkgmeta_phase="$yaml_key"
|
|
;;
|
|
[\ ]*"- "*)
|
|
yaml_line="${yaml_line#"${yaml_line%%[! ]*}"}" # trim leading whitespace
|
|
# Get the YAML list item.
|
|
yaml_listitem "$yaml_line"
|
|
if [[ "$pkgmeta_phase" == "ignore" || "$pkgmeta_phase" == "plain-copy" ]] && [[ $yaml_item == "$sub_path"* ]]; then
|
|
yaml_item=${yaml_item%"/*"} # trim dir glob
|
|
pattern=${yaml_item#$sub_path} # match relative to sub_path
|
|
if [ -d "$check_path$yaml_item" ]; then
|
|
pattern="$copy_path$pattern/*"
|
|
elif [ ! -f "$check_path$yaml_item" ]; then
|
|
# doesn't exist so match both a file and a path
|
|
pattern="$copy_path$pattern:$copy_path$pattern/*"
|
|
else
|
|
pattern="$copy_path$pattern"
|
|
fi
|
|
if [[ "$pkgmeta_phase" == "ignore" ]]; then
|
|
if [ -z "$ignore" ]; then
|
|
ignore="$pattern"
|
|
else
|
|
ignore="$ignore:$pattern"
|
|
fi
|
|
elif [[ "$pkgmeta_phase" == "plain-copy" ]]; then
|
|
if [ -z "$unchanged" ]; then
|
|
unchanged="$pattern"
|
|
else
|
|
unchanged="$unchanged:$pattern"
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
done < "$pkgmeta"
|
|
}
|
|
|
|
if [ -f "$pkgmeta_file" ]; then
|
|
if grep -q $'^[ ]*\t\+[[:blank:]]*[[:graph:]]' "$pkgmeta_file"; then
|
|
# Try to cut down on some troubleshooting pain.
|
|
echo "ERROR! Your pkgmeta file contains a leading tab. Only spaces are allowed for indentation in YAML files." >&2
|
|
grep -n $'^[ ]*\t\+[[:blank:]]*[[:graph:]]' "$pkgmeta_file" | sed $'s/\t/^I/g'
|
|
exit 1
|
|
fi
|
|
|
|
yaml_eof=
|
|
while [ -z "$yaml_eof" ]; do
|
|
IFS='' read -r yaml_line || yaml_eof="true"
|
|
# Skip commented out lines.
|
|
if [[ $yaml_line =~ ^[[:space:]]*\# ]]; then
|
|
continue
|
|
fi
|
|
# Strip any trailing CR character.
|
|
yaml_line="${yaml_line%"$carriage_return"}"
|
|
|
|
case $yaml_line in
|
|
[!\ ]*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
# Set the $pkgmeta_phase for stateful processing.
|
|
pkgmeta_phase="$yaml_key"
|
|
|
|
case $yaml_key in
|
|
enable-nolib-creation)
|
|
if [ "$yaml_value" = "yes" ]; then
|
|
enable_nolib_creation="true"
|
|
fi
|
|
;;
|
|
enable-toc-creation)
|
|
if [ "$yaml_value" = "yes" ]; then
|
|
split="true"
|
|
fi
|
|
;;
|
|
license-output)
|
|
license=$yaml_value
|
|
;;
|
|
manual-changelog)
|
|
changelog="$yaml_value"
|
|
manual_changelog="true"
|
|
;;
|
|
changelog-title)
|
|
project="$yaml_value"
|
|
;;
|
|
package-as)
|
|
package="$yaml_value"
|
|
;;
|
|
wowi-create-changelog)
|
|
if [ "$yaml_value" = "no" ]; then
|
|
wowi_gen_changelog=
|
|
fi
|
|
;;
|
|
wowi-convert-changelog)
|
|
if [ "$yaml_value" = "no" ]; then
|
|
wowi_convert_changelog=
|
|
fi
|
|
;;
|
|
wowi-archive-previous)
|
|
if [ "$yaml_value" = "no" ]; then
|
|
wowi_archive=
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
" "*)
|
|
yaml_line=${yaml_line#"${yaml_line%%[! ]*}"} # trim leading whitespace
|
|
case $yaml_line in
|
|
"- "*)
|
|
# Get the YAML list item.
|
|
yaml_listitem "$yaml_line"
|
|
case $pkgmeta_phase in
|
|
tools-used)
|
|
relations["${yaml_item,,}"]="tool"
|
|
;;
|
|
required-dependencies)
|
|
relations["${yaml_item,,}"]="requiredDependency"
|
|
;;
|
|
optional-dependencies)
|
|
relations["${yaml_item,,}"]="optionalDependency"
|
|
;;
|
|
embedded-libraries)
|
|
relations["${yaml_item,,}"]="embeddedLibrary"
|
|
;;
|
|
esac
|
|
;;
|
|
*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
case $pkgmeta_phase in
|
|
manual-changelog)
|
|
case $yaml_key in
|
|
filename)
|
|
changelog="$yaml_value"
|
|
manual_changelog="true"
|
|
;;
|
|
markup-type)
|
|
if [ "$yaml_value" = "markdown" ] || [ "$yaml_value" = "html" ]; then
|
|
changelog_markup="$yaml_value"
|
|
else
|
|
changelog_markup="text"
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
move-folders)
|
|
# Save project root directories
|
|
if [[ $yaml_value != *"/"* ]]; then
|
|
_mf_path="${yaml_key#*/}" # strip the package name
|
|
toc_root_paths["$topdir/$_mf_path"]="$yaml_value"
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done < "$pkgmeta_file"
|
|
fi
|
|
|
|
# Add untracked/ignored files to the ignore list
|
|
|
|
vcs_addignore() {
|
|
local _ignored_path="$1"
|
|
if [[ -d "$topdir/$_ignored_path" ]]; then
|
|
_ignored_path="$_ignored_path*"
|
|
fi
|
|
# Don't ignore a manual changelog generated for the build
|
|
if [[ -z $changelog || $_ignored_path != "$changelog" ]]; then
|
|
if [[ -z "$ignore" ]]; then
|
|
ignore="$_ignored_path"
|
|
else
|
|
ignore="$ignore:$_ignored_path"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
OLDIFS=$IFS
|
|
IFS=$'\n'
|
|
if [ "$repository_type" = "git" ]; then
|
|
for _vcs_ignore in $( git -C "$topdir" ls-files --others --directory ); do
|
|
vcs_addignore "$_vcs_ignore"
|
|
done
|
|
elif [ "$repository_type" = "svn" ]; then
|
|
for _vcs_ignore in $( cd "$topdir" && svn status --no-ignore --ignore-externals | awk '/^[?IX]/ { print $2 }' ); do
|
|
vcs_addignore "${_vcs_ignore//\\//}"
|
|
done
|
|
elif [ "$repository_type" = "hg" ]; then
|
|
for _vcs_ignore in $( hg --cwd "$topdir" status --ignored --unknown --no-status ); do
|
|
vcs_addignore "$_vcs_ignore"
|
|
done
|
|
fi
|
|
IFS=$OLDIFS
|
|
|
|
|
|
# Add .pkgmeta ignored files to the ignore list
|
|
parse_ignore "$pkgmeta_file"
|
|
|
|
###
|
|
### Process TOC file
|
|
###
|
|
|
|
set_info_toc_interface() {
|
|
local toc_path="$1"
|
|
local package_name="$2"
|
|
|
|
si_game_type_interface=()
|
|
si_game_type_interface_all=()
|
|
si_game_root_interface=
|
|
[[ -z $package_name ]] && return 0
|
|
|
|
local toc_name=${toc_path##*/}
|
|
|
|
local toc_suffix toc_file_game_type
|
|
if [[ $toc_name =~ "$package_name"[-_](Mainline|Classic|Vanilla|BCC|TBC|Wrath|WOTLKC|Cata|Mists|Camelot)\.toc$ ]]; then
|
|
toc_suffix="${BASH_REMATCH[1],,}"
|
|
toc_file_game_type="${game_flavor[$toc_suffix]}"
|
|
fi
|
|
|
|
local toc_file toc_version toc_game_type
|
|
toc_file=$(
|
|
# Remove BOM and CR and apply some non-version related TOC filters
|
|
[[ $file_type != "alpha" ]] && _tf_alpha="true"
|
|
sed -e $'1s/^\xEF\xBB\xBF//' -e $'s/\r//g' "$toc_path" | toc_filter alpha ${_tf_alpha:+true} | toc_filter debug true
|
|
)
|
|
|
|
toc_version=$( awk -F: '/^## Interface:/ { gsub(/[ \t]/, "", $2); print $2; exit }' <<< "$toc_file" )
|
|
if [[ -n $toc_version ]]; then
|
|
local toc_version_game_type
|
|
IFS=',' read -ra V <<< "$toc_version"
|
|
toc_to_file_type "${V[0]}" "toc_game_type"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_version_game_type"
|
|
if [[ -n ${si_game_type_interface_all[$toc_version_game_type]} ]]; then
|
|
si_game_type_interface_all[$toc_version_game_type]="${si_game_type_interface_all[$toc_version_game_type]}:$i"
|
|
else
|
|
si_game_type_interface_all[$toc_version_game_type]="$i"
|
|
fi
|
|
|
|
# Are the interface values all for one game type? (Using the base game type)
|
|
if [[ -n $toc_game_type ]]; then
|
|
toc_to_file_type "$i" "toc_version_game_type"
|
|
if [[ $toc_game_type != "$toc_version_game_type" ]]; then
|
|
toc_game_type=
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Splitting needs the fallback toc to be game typed, so make sure we're not mixing strategies
|
|
# if [[ -n $split && -z $toc_game_type ]]; then
|
|
# echo "$toc_name has multiple game type interface versions as the fallback ($toc_version) but creating TOC files is enabled." >&2
|
|
# echo "Add additional \"## Interface-[Type]:\" lines for each game type TOC file to create." >&2
|
|
# exit 1
|
|
# fi
|
|
|
|
toc_version=$( IFS=':' ; echo "${V[*]}" ) # swap delimiter
|
|
si_game_root_interface="$toc_version"
|
|
fi
|
|
if [[ -n $toc_suffix ]]; then
|
|
# Flavored, just validate the version
|
|
if [[ -z $toc_version ]]; then
|
|
echo "$toc_name is missing an interface version." >&2
|
|
exit 1
|
|
fi
|
|
if [[ $toc_suffix == "classic" ]]; then
|
|
# Special check for _Classic (the classic game family) to allow any classic version
|
|
IFS=':' read -ra V <<< "$toc_version"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_file_game_type"
|
|
if [[ $toc_file_game_type == "retail" || $toc_file_game_type == "forever" ]]; then
|
|
echo "$toc_name has an interface version ($i) that is not compatible with the game type \"Classic\"." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
elif [[ $toc_suffix == "mainline" ]]; then
|
|
# Special check for _Mainline (the "modern" game family) to allow retail (standard) and forever versions
|
|
IFS=':' read -ra V <<< "$toc_version"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_file_game_type"
|
|
if [[ $toc_file_game_type != "retail" && $toc_file_game_type != "forever" ]]; then
|
|
echo "$toc_name has an interface version ($i) that is not compatible with the game type \"Mainline\"." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
elif [[ $toc_file_game_type != "$toc_game_type" ]]; then
|
|
# Other suffixes are required to match the game type
|
|
echo "$toc_name has an interface version (${toc_version//:/,}) that is not compatible with the game version \"${toc_file_game_type}\"." >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
# Fallback
|
|
local game_type_toc_version toc_file_game_type
|
|
|
|
# Save the game type interface values
|
|
for type in "${!game_flavor[@]}"; do
|
|
game_type_toc_version=$( awk -F: 'tolower($0) ~ /^## interface-'"$type"':/ { gsub(/[ \t]/, "", $2); print $2; exit }' <<< "$toc_file" )
|
|
if [[ -n $game_type_toc_version ]]; then
|
|
type="${game_flavor[$type]}"
|
|
IFS=',' read -ra V <<< "$game_type_toc_version"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_file_game_type"
|
|
if [[ $toc_file_game_type != "$type" ]]; then
|
|
echo "$toc_name has an interface version ($i) that is not compatible with \"Interface-${type^}\"." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
game_type_toc_version=$( IFS=':' ; echo "${V[*]}" ) # swap delimiter
|
|
si_game_type_interface[$type]="$game_type_toc_version"
|
|
si_game_type_interface_all[$type]="$game_type_toc_version"
|
|
fi
|
|
done
|
|
|
|
# Use the game type if set, otherwise default to retail
|
|
game_type_toc_version="${si_game_type_interface_all[${game_type:-retail}]}"
|
|
# No "Interface:" value or game type doesn't match, try to use the "Interface-Type:" value
|
|
if [[ -z $toc_version ]] || [[ -n $game_type && -n $game_type_toc_version && $game_type_toc_version != "$toc_version" ]]; then
|
|
toc_version="$game_type_toc_version"
|
|
toc_game_type="${game_type:-retail}"
|
|
fi
|
|
|
|
# Check @non-@ blocks for an interface matching the game type (DEPRECATED, remove this in v3)
|
|
# XXX toc_game_type is the base game type, so we check si_game_type_interface_all for the manually set game type (band-aid for titan⊃wrath)
|
|
if [[ -z $toc_version ]] || [[ -n $game_type && $toc_game_type != "$game_type" && -z ${si_game_type_interface_all[$game_type]} ]]; then
|
|
toc_game_type="$game_type"
|
|
local game_type_toc_prefix
|
|
case $toc_game_type in
|
|
classic) game_type_toc_prefix="11[345]" ;;
|
|
bcc) game_type_toc_prefix="20" ;;
|
|
wrath) game_type_toc_prefix="30" ;;
|
|
cata) game_type_toc_prefix="40" ;;
|
|
mists) game_type_toc_prefix="50" ;;
|
|
titan) game_type_toc_prefix="380" ;;
|
|
forever) game_type_toc_prefix="16[0-9]" ;;
|
|
*) game_type_toc_prefix=
|
|
esac
|
|
if [[ -n $game_type_toc_prefix ]]; then
|
|
toc_version=$( sed -n '/@non-[-a-z]*@/,/@end-non-[-a-z]*@/{//b;p}' <<< "$toc_file" | awk -F: '/#[ \t]*## Interface:[ \t]*('"$game_type_toc_prefix"')/ { gsub(/[ \t]/, "", $2); print $2; exit }' )
|
|
IFS=',' read -ra V <<< "$toc_version"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_file_game_type"
|
|
if [[ $toc_file_game_type != "$toc_game_type" ]]; then
|
|
echo "$toc_name has an interface version ($i) that is not compatible with the game version \"${game_flavor[$toc_game_type]}\"." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
toc_version=$( IFS=':' ; echo "${V[*]}" ) # swap delimiter
|
|
|
|
# This becomes the actual interface version after replacements
|
|
si_game_root_interface="$toc_version"
|
|
si_game_type_interface_all[$toc_game_type]="$toc_version"
|
|
fi
|
|
fi
|
|
|
|
# Only game type TOC files will be created, so a fallback interface version isn't needed
|
|
if [[ -z $toc_version && -n $split && -z $game_type && ${#si_game_type_interface[@]} -gt 0 ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# End of the line
|
|
if [[ -z $toc_version ]]; then
|
|
if [[ -z $toc_game_type ]]; then
|
|
echo "$toc_name is missing an interface version." >&2
|
|
else
|
|
echo "$toc_name does not have an interface version that is compatible with the game version \"$toc_game_type\"." >&2
|
|
fi
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
set_toc_project_info() {
|
|
local toc_path="$1"
|
|
# Get the title of the addon for the changelog
|
|
if [ -z "$project" ]; then
|
|
project=$( sed -e $'1s/^\xEF\xBB\xBF//' -e $'s/\r//g' "$toc_path" | awk '/^## Title:/ { print $0; exit }' | sed -e 's/|c[0-9A-Fa-f]\{8\}//g' -e 's/|r//g' -e 's/|T[^|]*|t//g' -e 's/## Title[[:space:]]*:[[:space:]]*\(.*\)/\1/' -e 's/[[:space:]]*$//' )
|
|
fi
|
|
# Get project IDs for uploading
|
|
if [ -z "$slug" ]; then
|
|
slug=$( sed -e $'1s/^\xEF\xBB\xBF//' -e $'s/\r//g' "$toc_path" | awk -F: '/^## X-Curse-Project-ID:/ { gsub(/[ \t]/, "", $2); print $2; exit }' )
|
|
fi
|
|
if [ -z "$addonid" ]; then
|
|
addonid=$( sed -e $'1s/^\xEF\xBB\xBF//' -e $'s/\r//g' "$toc_path" | awk -F: '/^## X-WoWI-ID:/ { gsub(/[ \t]/, "", $2); print $2; exit }' )
|
|
fi
|
|
if [ -z "$wagoid" ]; then
|
|
wagoid=$( sed -e $'1s/^\xEF\xBB\xBF//' -e $'s/\r//g' "$toc_path" | awk -F: '/^## X-Wago-ID:/ { gsub(/[ \t]/, "", $2); print $2; exit }' )
|
|
fi
|
|
}
|
|
|
|
set_build_version() {
|
|
if [[ -z $game_version ]]; then
|
|
local toc_version toc_game_type path_interface path_version
|
|
for path in "${toc_paths[@]}"; do
|
|
if [[ -z "$split" && -z "$game_type" ]]; then
|
|
# no split and no game type means we should use the root interface value
|
|
toc_version="${toc_root_interface[$path]}"
|
|
else
|
|
toc_version="${toc_interfaces[$path]}"
|
|
fi
|
|
IFS=':' read -ra V <<< "$toc_version"
|
|
for i in "${V[@]}"; do
|
|
toc_to_type "$i" "toc_game_type"
|
|
# add the version for every root addon
|
|
if [[ -z $game_type || $game_type == "$toc_game_type" ]]; then
|
|
path_interface="$i"
|
|
path_version=$( printf "%d.%d.%d" $(( 10#${i:0: -4} )) $(( 10#${i: -4:2} )) $(( 10#${i: -2:2} )) )
|
|
if [[ -n ${game_type_interface[$toc_game_type]} ]]; then
|
|
if [[ ":${game_type_interface[$toc_game_type]}:" != *":$path_interface:"* ]]; then
|
|
game_type_interface[$toc_game_type]="${game_type_interface[$toc_game_type]}:$path_interface"
|
|
game_type_version[$toc_game_type]="${game_type_version[$toc_game_type]}:$path_version"
|
|
fi
|
|
else
|
|
game_type_interface[$toc_game_type]="$path_interface"
|
|
game_type_version[$toc_game_type]="$path_version"
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
|
|
# Set the game versions shown in the summary
|
|
game_version=$(
|
|
if [[ -n $game_type ]]; then
|
|
readarray -t sorted < <( printf '%s\n' "${game_type_version[$game_type]//:/$'\n'}" | sort -Vr )
|
|
else
|
|
readarray -t sorted < <( printf '%s\n' "${game_type_version[@]//:/$'\n'}" | sort -Vr )
|
|
fi
|
|
IFS=',' ; echo "${sorted[*]}"
|
|
)
|
|
|
|
# Set the game type when we only have one game version
|
|
if [[ -z $game_type && ${#game_type_version[@]} -eq 1 ]]; then
|
|
game_type="${!game_type_version[*]}"
|
|
fi
|
|
}
|
|
|
|
# Set the package name from a TOC file name
|
|
if [[ -z "$package" ]]; then
|
|
# grab the shortest toc file in the top directory
|
|
package=$( cd "$topdir" && find ./*.toc -maxdepth 0 2>/dev/null | awk '{ print length(), substr($0, 3) }' | sort -n | cut -d" " -f2- | head -n1 )
|
|
if [[ -z "$package" ]]; then
|
|
echo "Could not find an addon TOC file. In another directory? Set 'package-as' in .pkgmeta" >&2
|
|
exit 1
|
|
fi
|
|
package=${package%.toc}
|
|
if [[ $package =~ ^(.*)([-_](Mainline|Classic|Vanilla|BCC|TBC|Wrath|WOTLKC|Cata|Mists|Camelot))$ ]]; then
|
|
echo "Ambiguous addon name. No fallback TOC file or addon name includes an expansion suffix (${BASH_REMATCH[2]}). Set 'package-as' in .pkgmeta" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Parse the project root TOC files for info first
|
|
for toc_path in "$topdir/$package"{,-Mainline,_Mainline,-Classic,_Classic,-Vanilla,_Vanilla,-BCC,_BCC,-TBC,_TBC,-Wrath,_Wrath,-WOTLKC,_WOTLKC,-Cata,_Cata,-Mists,_Mists,-Camelot,_Camelot}.toc; do
|
|
if [[ -f "$toc_path" ]]; then
|
|
set_toc_project_info "$toc_path"
|
|
toc_paths+=("$toc_path")
|
|
toc_root_paths["$topdir"]="$package"
|
|
fi
|
|
done
|
|
# Try parsing the project addon in move-folders for info next
|
|
for path in "${!toc_root_paths[@]}"; do
|
|
if [[ ${toc_root_paths[$path]} == "$package" && $path != "$topdir" ]]; then
|
|
for toc_path in "$path/$package"{,-Mainline,_Mainline,-Classic,_Classic,-Vanilla,_Vanilla,-BCC,_BCC,-TBC,_TBC,-Wrath,_Wrath,-WOTLKC,_WOTLKC,-Cata,_Cata,-Mists,_Mists,-Camelot,_Camelot}.toc; do
|
|
if [[ -f "$toc_path" ]]; then
|
|
set_toc_project_info "$toc_path"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
# Parse project TOC files
|
|
for path in "${!toc_root_paths[@]}"; do
|
|
for toc_path in "$path/${toc_root_paths[$path]}"{,-Mainline,_Mainline,-Classic,_Classic,-Vanilla,_Vanilla,-BCC,_BCC,-TBC,_TBC,-Wrath,_Wrath,-WOTLKC,_WOTLKC,-Cata,_Cata,-Mists,_Mists,-Camelot,_Camelot}.toc; do
|
|
if [[ -f "$toc_path" ]]; then
|
|
set_toc_project_info "$toc_path"
|
|
set_info_toc_interface "$toc_path" "${toc_root_paths[$path]}"
|
|
toc_root_interface[$toc_path]="$si_game_root_interface"
|
|
toc_interfaces[$toc_path]=$( IFS=':' ; echo "${si_game_type_interface_all[*]}" )
|
|
if [[ " ${toc_paths[*]} " != *" $toc_path "* ]]; then
|
|
toc_paths+=("$toc_path")
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [[ ${#toc_interfaces[@]} -eq 0 ]]; then
|
|
echo "Could not find an addon TOC file. In another directory? Make sure it matches the 'package-as' in .pkgmeta" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$split" ]]; then
|
|
if [[ "${toc_interfaces[*]}" != *":"* ]]; then
|
|
echo "Creating TOC files is enabled but there is only one TOC interface version per file?" >&2
|
|
for path in "${toc_paths[@]}"; do
|
|
[[ -n ${toc_interfaces[$path]} ]] && echo " ${path##$topdir/} [${toc_interfaces[$path]}]" >&2
|
|
done
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
set_build_version
|
|
|
|
if [[ -z "$game_version" ]]; then
|
|
echo "There was a problem setting the build version. Do you have a base \"# Interface:\" line in your TOC files?" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Unset project ids if they are set to 0
|
|
[ "$slug" = "0" ] && slug=
|
|
[ "$addonid" = "0" ] && addonid=
|
|
[ "$wagoid" = "0" ] && wagoid=
|
|
|
|
echo "Packaging $package"
|
|
if [ -n "$project_version" ]; then
|
|
echo "Current version: $project_version"
|
|
fi
|
|
if [ -n "$previous_version" ]; then
|
|
echo "Previous version: $previous_version"
|
|
fi
|
|
(
|
|
if [[ -n $game_type ]]; then
|
|
[[ $game_type == "retail" ]] && version="retail " || version="non-retail version-${game_type} "
|
|
elif [[ ${#game_type_version[@]} -gt 1 ]]; then
|
|
version="multi-version "
|
|
fi
|
|
[ "$file_type" = "alpha" ] && alpha="alpha" || alpha="non-alpha"
|
|
echo "Build type: ${version}${alpha} non-debug${nolib:+ nolib}"
|
|
echo "Game version: ${game_version//,/, }"
|
|
echo
|
|
)
|
|
if [[ "$slug" =~ ^[0-9]+$ ]]; then
|
|
project_site="https://wow.curseforge.com"
|
|
echo "CurseForge ID: $slug${cf_token:+ [token set]}"
|
|
fi
|
|
if [ -n "$addonid" ]; then
|
|
echo "WoWInterface ID: $addonid${wowi_token:+ [token set]}"
|
|
fi
|
|
if [ -n "$wagoid" ]; then
|
|
echo "Wago ID: $wagoid${wago_token:+ [token set]}"
|
|
fi
|
|
if [ -n "$project_github_slug" ]; then
|
|
echo "GitHub: $project_github_slug${github_token:+ [token set]}"
|
|
fi
|
|
if [ -n "$project_site" ] || [ -n "$addonid" ] || [ -n "$wagoid" ] || [ -n "$project_github_slug" ]; then
|
|
echo
|
|
fi
|
|
echo "Checkout directory: $topdir"
|
|
echo "Release directory: $releasedir"
|
|
echo
|
|
|
|
# Set $pkgdir to the path of the package directory inside $releasedir.
|
|
pkgdir="$releasedir/$package"
|
|
if [ -d "$pkgdir" ] && [ -z "$overwrite" ]; then
|
|
#echo "Removing previous package directory: $pkgdir"
|
|
rm -fr "$pkgdir"
|
|
fi
|
|
if [ ! -d "$pkgdir" ]; then
|
|
mkdir -p "$pkgdir"
|
|
fi
|
|
|
|
# Set the contents of the addon zipfile.
|
|
zip_root_dirs+=("$package")
|
|
|
|
###
|
|
### Create filters for pass-through processing of files to replace repository keywords.
|
|
###
|
|
|
|
# Filter for simple repository keyword replacement.
|
|
vcs_filter() {
|
|
sed \
|
|
-e "s/@project-revision@/$si_project_revision/g" \
|
|
-e "s/@project-hash@/$si_project_hash/g" \
|
|
-e "s/@project-abbreviated-hash@/$si_project_abbreviated_hash/g" \
|
|
-e "s/@project-author@/$( escape_substr "$si_project_author" )/g" \
|
|
-e "s/@project-date-iso@/$si_project_date_iso/g" \
|
|
-e "s/@project-date-integer@/$si_project_date_integer/g" \
|
|
-e "s/@project-timestamp@/$si_project_timestamp/g" \
|
|
-e "s/@project-version@/$( escape_substr "$si_project_version" )/g" \
|
|
-e "s/@file-revision@/$si_file_revision/g" \
|
|
-e "s/@file-hash@/$si_file_hash/g" \
|
|
-e "s/@file-abbreviated-hash@/$si_file_abbreviated_hash/g" \
|
|
-e "s/@file-author@/$( escape_substr "$si_file_author" )/g" \
|
|
-e "s/@file-date-iso@/$si_file_date_iso/g" \
|
|
-e "s/@file-date-integer@/$si_file_date_integer/g" \
|
|
-e "s/@file-timestamp@/$si_file_timestamp/g" \
|
|
-e "s/@build-date@/$si_build_date/g" \
|
|
-e "s/@build-date-iso@/$si_build_date_iso/g" \
|
|
-e "s/@build-date-integer@/$si_build_date_integer/g" \
|
|
-e "s/@build-timestamp@/$si_build_timestamp/g"
|
|
}
|
|
|
|
# Find URL of localization api.
|
|
set_localization_url() {
|
|
localization_url=
|
|
if [ -n "$slug" ] && [ -n "$cf_token" ] && [ -n "$project_site" ]; then
|
|
localization_url="${project_site}/api/projects/$slug/localization/export"
|
|
fi
|
|
if [ -z "$localization_url" ] && find "$topdir" -path '*/.*' -prune -o -name "*.lua" -print0 | xargs -0 grep -q "@localization"; then
|
|
echo "Skipping localization! Missing CurseForge API token and/or project id is invalid."
|
|
echo
|
|
fi
|
|
}
|
|
|
|
# Filter to handle @localization@ repository keyword replacement.
|
|
# https://support.curseforge.com/support/solutions/articles/9000197354-localization-substitutions
|
|
declare -A unlocalized_values=( ["english"]="ShowPrimary" ["comment"]="ShowPrimaryAsComment" ["blank"]="ShowBlankAsComment" ["ignore"]="Ignore" )
|
|
localization_filter() {
|
|
_ul_eof=
|
|
while [ -z "$_ul_eof" ]; do
|
|
IFS='' read -r _ul_line || _ul_eof="true"
|
|
# Strip any trailing CR character.
|
|
_ul_line=${_ul_line%$carriage_return}
|
|
case $_ul_line in
|
|
*@localization\(*\)@*)
|
|
_ul_lang=
|
|
_ul_namespace=
|
|
_ul_singlekey=
|
|
_ul_tablename="L"
|
|
# Get the prefix of the line before the comment.
|
|
_ul_prefix=${_ul_line%%@localization(*}
|
|
_ul_prefix=${_ul_prefix%%--*}
|
|
# Strip everything but the localization parameters.
|
|
_ul_params=${_ul_line#*@localization(}
|
|
_ul_params=${_ul_params%)@}
|
|
# Sanitize the params a bit. (namespaces are restricted to [a-zA-Z0-9_], separated by [./:])
|
|
_ul_params=${_ul_params// /}
|
|
_ul_params=${_ul_params//,/, }
|
|
# Pull the locale language first (mainly for warnings).
|
|
_ul_lang="enUS"
|
|
if [[ $_ul_params == *"locale=\""* ]]; then
|
|
_ul_lang=${_ul_params##*locale=\"}
|
|
_ul_lang=${_ul_lang:0:4}
|
|
_ul_lang=${_ul_lang%%\"*}
|
|
else
|
|
echo " Warning! No locale set, using enUS." >&3
|
|
fi
|
|
# Generate a URL parameter string from the localization parameters.
|
|
# https://support.curseforge.com/support/solutions/articles/9000197321-curseforge-upload-api#Localization
|
|
_ul_url_params=""
|
|
# shellcheck disable=SC2086 # split on spaces to iterate key=value pairs
|
|
set -- ${_ul_params}
|
|
for _ul_param; do
|
|
_ul_key=${_ul_param%%=*}
|
|
_ul_value=${_ul_param#*=}
|
|
_ul_value=${_ul_value%,*}
|
|
_ul_value=${_ul_value#*\"}
|
|
_ul_value=${_ul_value%\"*}
|
|
case ${_ul_key} in
|
|
escape-non-ascii)
|
|
if [ "$_ul_value" = "true" ]; then
|
|
_ul_url_params="${_ul_url_params}&escape-non-ascii-characters=true"
|
|
fi
|
|
;;
|
|
format)
|
|
if [ "$_ul_value" = "lua_table" ]; then
|
|
_ul_url_params="${_ul_url_params}&export-type=Table"
|
|
fi
|
|
;;
|
|
handle-unlocalized)
|
|
if [ "$_ul_value" != "english" ] && [ -n "${unlocalized_values[$_ul_value]}" ]; then
|
|
_ul_url_params="${_ul_url_params}&unlocalized=${unlocalized_values[$_ul_value]}"
|
|
fi
|
|
;;
|
|
handle-subnamespaces)
|
|
if [ "$_ul_value" = "concat" ]; then # concat with /
|
|
_ul_url_params="${_ul_url_params}&concatenante-subnamespaces=true"
|
|
elif [ "$_ul_value" = "subtable" ]; then
|
|
echo " ($_ul_lang) Warning! ${_ul_key}=\"${_ul_value}\" is not supported. Include each full subnamespace, comma delimited." >&3
|
|
fi
|
|
;;
|
|
key)
|
|
# _ul_params was stripped of spaces, so reparse the line for the key
|
|
_ul_singlekey=${_ul_line#*@localization(}
|
|
_ul_singlekey=${_ul_singlekey#*key=\"}
|
|
_ul_singlekey=${_ul_singlekey%%\",*}
|
|
_ul_singlekey=${_ul_singlekey%%\")@*}
|
|
;;
|
|
locale)
|
|
_ul_lang=$_ul_value
|
|
;;
|
|
namespace)
|
|
# reparse to get all namespaces if multiple
|
|
_ul_namespace=${_ul_params##*namespace=\"}
|
|
_ul_namespace=${_ul_namespace%%\"*}
|
|
_ul_namespace=${_ul_namespace//, /,}
|
|
_ul_url_params="${_ul_url_params}&namespaces=${_ul_namespace}"
|
|
_ul_namespace="/${_ul_namespace}"
|
|
;;
|
|
namespace-delimiter)
|
|
if [ "$_ul_value" != "/" ]; then
|
|
echo " ($_ul_lang) Warning! ${_ul_key}=\"${_ul_value}\" is not supported." >&3
|
|
fi
|
|
;;
|
|
prefix-values)
|
|
echo " ($_ul_lang) Warning! \"${_ul_key}\" is not supported." >&3
|
|
;;
|
|
same-key-is-true)
|
|
if [ "$_ul_value" = "true" ]; then
|
|
_ul_url_params="${_ul_url_params}&true-if-value-equals-key=true"
|
|
fi
|
|
;;
|
|
table-name)
|
|
if [ "$_ul_value" != "L" ]; then
|
|
_ul_tablename="$_ul_value"
|
|
_ul_url_params="${_ul_url_params}&table-name=${_ul_value}"
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$_cdt_localization" ] || [ -z "$localization_url" ]; then
|
|
echo " Skipping localization (${_ul_lang}${_ul_namespace})" >&3
|
|
|
|
# If the line isn't a TOC entry, print anything before the keyword.
|
|
if [[ $_ul_line != "## "* ]]; then
|
|
if [ -n "$_ul_eof" ]; then
|
|
echo -n "$_ul_prefix"
|
|
else
|
|
echo "$_ul_prefix"
|
|
fi
|
|
fi
|
|
else
|
|
_ul_url="${localization_url}?lang=${_ul_lang}${_ul_url_params}"
|
|
echo " Adding ${_ul_lang}${_ul_namespace}" >&3
|
|
|
|
if [ -z "$_ul_singlekey" ]; then
|
|
# Write text that preceded the substitution.
|
|
echo -n "$_ul_prefix"
|
|
|
|
# Fetch the localization data, but don't output anything if there is an error.
|
|
curl -sL -H "x-api-token: $cf_token" "${_ul_url}" | awk -v url="$_ul_url" '/^{"error/ { o=" \033[01;31mError! "$0"\033[0m\n "url; print o >"/dev/fd/3"; exit 1 } /<!(DOCTYPE|doctype)/ { print " \033[01;31mError! Invalid output\033[0m\n "url >"/dev/fd/3"; exit 1 } /^<(HTML|html)>/ { print " \033[01;31mError! Invalid output\033[0m\n "url >"/dev/fd/3"; exit 1 } /^'"$_ul_tablename"' = '"$_ul_tablename"' or \{\}/ { next } { print }' || exit 1
|
|
|
|
# Insert a trailing blank line to match CF packager.
|
|
if [ -z "$_ul_eof" ]; then
|
|
echo ""
|
|
fi
|
|
else
|
|
# Parse out a single phrase. This is kind of expensive, but caching would be way too much effort to optimize for what is basically an edge case.
|
|
_ul_value=$( curl -sL -H "x-api-token: $cf_token" "${_ul_url}" | awk -v url="$_ul_url" '/^{"error/ { o=" \033[01;31mError! "$0"\033[0m\n "url; print o >"/dev/fd/3"; exit 1 } /<!(DOCTYPE|doctype)/ { print " \033[01;31mError! Invalid output\033[0m\n "url >"/dev/fd/3"; exit 1 } /^<(HTML|html)>/ { print " \033[01;31mError! Invalid output\033[0m\n "url >"/dev/fd/3"; exit 1 } { print }' | sed -n '/L\["'"$_ul_singlekey"'"\]/p' | sed 's/^.* = "\(.*\)"/\1/' )
|
|
if [ -n "$_ul_value" ] && [ "$_ul_value" != "$_ul_singlekey" ]; then
|
|
# The result is different from the base value so print out the line.
|
|
echo "${_ul_prefix}${_ul_value}${_ul_line##*)@}"
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
*)
|
|
if [ -n "$_ul_eof" ]; then
|
|
echo -n "$_ul_line"
|
|
else
|
|
echo "$_ul_line"
|
|
fi
|
|
esac
|
|
done
|
|
}
|
|
|
|
lua_filter() {
|
|
local keyword="$1"
|
|
local width
|
|
case $keyword in
|
|
alpha) width="=" ;;
|
|
debug) width="==" ;;
|
|
retail|version-*) width="====" ;;
|
|
*) width="==="
|
|
esac
|
|
sed \
|
|
-e "s/--@${keyword}@/--[${width}[@${keyword}@/g" \
|
|
-e "s/--@end-${keyword}@/--@end-${keyword}@]${width}]/g" \
|
|
-e "s/--\[===\[@non-${keyword}@/--@non-${keyword}@/g" \
|
|
-e "s/--@end-non-${keyword}@\]===\]/--@end-non-${keyword}@/g"
|
|
}
|
|
|
|
toc_interface_filter() {
|
|
local toc_version="${1//:/, }"
|
|
local current_toc_version="${2//:/, }"
|
|
# TOC version isn't what is set in the TOC file
|
|
if [[ -n "$toc_version" && "$current_toc_version" != "$toc_version" ]]; then
|
|
# Always remove BOM so ^ works
|
|
if [ -n "$current_toc_version" ]; then # rewrite
|
|
sed -e $'1s/^\xEF\xBB\xBF//' -e 's/^## Interface:.*$/## Interface: '"$toc_version"'/' -e '/^## Interface-/d'
|
|
else # add
|
|
sed -e $'1s/^\xEF\xBB\xBF//' -e '1i\
|
|
## Interface: '"$toc_version" -e '/^## Interface-/d'
|
|
fi
|
|
if [[ -z "$split" ]]; then
|
|
echo " Set Interface to ${toc_version}" >&3
|
|
fi
|
|
else # cleanup
|
|
sed -e $'1s/^\xEF\xBB\xBF//' -e '/^## Interface-/d'
|
|
fi
|
|
}
|
|
|
|
xml_filter() {
|
|
sed \
|
|
-e "s/<!--@$1@-->/<!--@$1@/g" \
|
|
-e "s/<!--@end-$1@-->/@end-$1@-->/g" \
|
|
-e "s/<!--@non-$1@/<!--@non-$1@-->/g" \
|
|
-e "s/@end-non-$1@-->/<!--@end-non-$1@-->/g"
|
|
}
|
|
|
|
do_not_package_filter() {
|
|
case $1 in
|
|
lua) sed '/--@do-not-package@/,/--@end-do-not-package@/d' ;;
|
|
toc) sed '/#@do-not-package@/,/#@end-do-not-package@/d' ;;
|
|
xml) sed '/<!--@do-not-package@-->/,/<!--@end-do-not-package@-->/d' ;;
|
|
esac
|
|
}
|
|
|
|
line_ending_filter() {
|
|
local _lef_eof _lef_line
|
|
while [ -z "$_lef_eof" ]; do
|
|
IFS='' read -r _lef_line || _lef_eof="true"
|
|
# Strip any trailing CR character.
|
|
_lef_line=${_lef_line%$carriage_return}
|
|
if [ -n "$_lef_eof" ]; then
|
|
# Preserve EOF not preceded by newlines.
|
|
echo -n "$_lef_line"
|
|
else
|
|
case $line_ending in
|
|
dos) printf "%s\r\n" "$_lef_line" ;; # Terminate lines with CR LF.
|
|
unix) printf "%s\n" "$_lef_line" ;; # Terminate lines with LF.
|
|
esac
|
|
fi
|
|
done
|
|
}
|
|
|
|
###
|
|
### Copy files from the working directory into the package directory.
|
|
###
|
|
|
|
# Copy of the contents of the source directory into the destination directory.
|
|
# Dotfiles and any files matching the ignore pattern are skipped. Copied files
|
|
# are subject to repository keyword replacement.
|
|
#
|
|
copy_directory_tree() {
|
|
_cdt_alpha=
|
|
_cdt_debug=
|
|
_cdt_ignored_patterns=
|
|
_cdt_localization=
|
|
_cdt_nolib=
|
|
_cdt_do_not_package=
|
|
_cdt_unchanged_patterns=
|
|
_cdt_gametype=
|
|
_cdt_external=
|
|
_cdt_split=
|
|
OPTIND=1
|
|
while getopts :adi:lnpu:g:eS _cdt_opt "$@"; do
|
|
# shellcheck disable=SC2220 # not user facing, don't need to handle invalid options
|
|
case $_cdt_opt in
|
|
a) _cdt_alpha="true" ;;
|
|
d) _cdt_debug="true" ;;
|
|
i) _cdt_ignored_patterns=$OPTARG ;;
|
|
l) _cdt_localization="true"
|
|
set_localization_url
|
|
;;
|
|
n) _cdt_nolib="true" ;;
|
|
p) _cdt_do_not_package="true" ;;
|
|
u) _cdt_unchanged_patterns=$OPTARG ;;
|
|
g) _cdt_gametype=$OPTARG ;;
|
|
e) _cdt_external="true" ;;
|
|
S) _cdt_split="true" ;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
_cdt_srcdir=$1
|
|
_cdt_destdir=$2
|
|
|
|
if [ -z "$_cdt_external" ]; then
|
|
start_group "Copying files into ${_cdt_destdir#$topdir/}:" "copy"
|
|
else # don't nest groups
|
|
echo "Copying files into ${_cdt_destdir#$topdir/}:"
|
|
fi
|
|
if [ ! -d "$_cdt_destdir" ]; then
|
|
mkdir -p "$_cdt_destdir"
|
|
fi
|
|
|
|
# Create a "find" command to list all of the files in the current directory, minus any ones we need to prune.
|
|
_cdt_find_cmd="find ."
|
|
# Prune everything that begins with a dot except for the current directory ".".
|
|
_cdt_find_cmd+=" \( -name \".*\" -a \! -name \".\" \) -prune"
|
|
# Prune the destination directory if it is a subdirectory of the source directory.
|
|
_cdt_dest_subdir=${_cdt_destdir#${_cdt_srcdir}/}
|
|
case $_cdt_dest_subdir in
|
|
/*) ;;
|
|
*) _cdt_find_cmd+=" -o -path \"./$_cdt_dest_subdir\" -prune" ;;
|
|
esac
|
|
# Print the filename, but suppress the current directory ".".
|
|
_cdt_find_cmd+=" -o \! -name \".\" -print"
|
|
|
|
local file
|
|
while read -r file; do # <( cd "$_cdt_srcdir" && eval "$_cdt_find_cmd" )
|
|
file=${file#./}
|
|
_cdt_source_file="$_cdt_srcdir/$file"
|
|
if [ -f "$_cdt_source_file" ]; then
|
|
_cdt_skip_copy=
|
|
_cdt_only_copy=
|
|
# Prefix external files with the relative pkgdir path
|
|
_cdt_check_file=$file
|
|
if [ -n "${_cdt_destdir#$pkgdir}" ]; then
|
|
_cdt_check_file="${_cdt_destdir#$pkgdir/}/$file"
|
|
fi
|
|
# Skip files matching the colon-separated "ignored" shell wildcard patterns.
|
|
if match_pattern "$_cdt_check_file" "$_cdt_ignored_patterns"; then
|
|
_cdt_skip_copy="true"
|
|
fi
|
|
# Don't process files that match the colon-separated "unchanged" shell wildcard patterns.
|
|
if match_pattern "$_cdt_check_file" "$_cdt_unchanged_patterns"; then
|
|
_cdt_only_copy="true"
|
|
fi
|
|
# Copy unskipped files into $_cdt_destdir.
|
|
if [ -n "$_cdt_skip_copy" ]; then
|
|
echo " Ignoring: $file"
|
|
else
|
|
_cdt_subdir=${file%/*}
|
|
if [ "$_cdt_subdir" != "$file" ]; then
|
|
mkdir -p "$_cdt_destdir/$_cdt_subdir"
|
|
fi
|
|
# Check for marked hard embedded libraries
|
|
if [[ -z $_cdt_external ]]; then
|
|
_cdt_external_slug=
|
|
if [[ $_cdt_source_file == *".lua" ]] && _cdt_external_slug=$( grep -io "@curseforge-project-slug[[:blank:]]*:[[:blank:]]*[^@]\+@" "$_cdt_source_file"); then
|
|
_cdt_external_slug="${_cdt_external_slug//[[:blank:]@]/}"
|
|
_cdt_external_slug="${_cdt_external_slug##*:}"
|
|
if [[ ${_cdt_external_slug,,} == "${package,,}" ]]; then
|
|
_cdt_external_slug=
|
|
fi
|
|
if [[ -n $_cdt_external_slug ]]; then
|
|
relations["${_cdt_external_slug,,}"]="embeddedLibrary"
|
|
fi
|
|
fi
|
|
fi
|
|
# Check if the file matches a pattern for keyword replacement.
|
|
if [ -n "$_cdt_only_copy" ] || ! match_pattern "$file" "*.lua:*.md:*.toc:*.txt:*.xml"; then
|
|
echo " Copying: $file (unchanged)${_cdt_external_slug:+(embedded: "$_cdt_external_slug")}"
|
|
cp "$_cdt_source_file" "$_cdt_destdir/$_cdt_subdir"
|
|
else
|
|
_cdt_file_gametype="$_cdt_gametype"
|
|
# Set the filters for replacement based on file extension.
|
|
_cdt_filters="vcs_filter"
|
|
case $file in
|
|
*.lua)
|
|
[[ -n $_cdt_do_not_package ]] && _cdt_filters+="|do_not_package_filter lua"
|
|
[[ -n $_cdt_debug ]] && _cdt_filters+="|lua_filter debug"
|
|
[[ -n $_cdt_alpha ]] && _cdt_filters+="|lua_filter alpha"
|
|
if [[ -n $_cdt_file_gametype ]]; then
|
|
[[ $_cdt_file_gametype != "retail" ]] && _cdt_filters+="|lua_filter version-retail|lua_filter retail"
|
|
[[ $_cdt_file_gametype != "classic" ]] && _cdt_filters+="|lua_filter version-classic|lua_filter version-vanilla"
|
|
[[ $_cdt_file_gametype != "bcc" ]] && _cdt_filters+="|lua_filter version-bcc"
|
|
[[ $_cdt_file_gametype != "wrath" ]] && _cdt_filters+="|lua_filter version-wrath"
|
|
[[ $_cdt_file_gametype != "cata" ]] && _cdt_filters+="|lua_filter version-cata"
|
|
[[ $_cdt_file_gametype != "mists" ]] && _cdt_filters+="|lua_filter version-mists"
|
|
[[ $_cdt_file_gametype != "titan" ]] && _cdt_filters+="|lua_filter version-titan"
|
|
[[ $_cdt_file_gametype != "forever" ]] && _cdt_filters+="|lua_filter version-forever"
|
|
fi
|
|
[[ -n $_cdt_localization ]] && grep -q "@localization" "$_cdt_source_file" && _cdt_filters+="|localization_filter"
|
|
;;
|
|
*.xml)
|
|
[[ -n $_cdt_do_not_package ]] && _cdt_filters+="|do_not_package_filter xml"
|
|
[[ -n $_cdt_nolib ]] && _cdt_filters+="|xml_filter no-lib-strip"
|
|
[[ -n $_cdt_debug ]] && _cdt_filters+="|xml_filter debug"
|
|
[[ -n $_cdt_alpha ]] && _cdt_filters+="|xml_filter alpha"
|
|
if [[ -n $_cdt_file_gametype ]]; then
|
|
[[ $_cdt_file_gametype != "retail" ]] && _cdt_filters+="|xml_filter version-retail|xml_filter retail"
|
|
[[ $_cdt_file_gametype != "classic" ]] && _cdt_filters+="|xml_filter version-classic|xml_filter version-vanilla"
|
|
[[ $_cdt_file_gametype != "bcc" ]] && _cdt_filters+="|xml_filter version-bcc"
|
|
[[ $_cdt_file_gametype != "wrath" ]] && _cdt_filters+="|xml_filter version-wrath"
|
|
[[ $_cdt_file_gametype != "cata" ]] && _cdt_filters+="|xml_filter version-cata"
|
|
[[ $_cdt_file_gametype != "mists" ]] && _cdt_filters+="|xml_filter version-mists"
|
|
[[ $_cdt_file_gametype != "titan" ]] && _cdt_filters+="|xml_filter version-titan"
|
|
[[ $_cdt_file_gametype != "forever" ]] && _cdt_filters+="|xml_filter version-forever"
|
|
fi
|
|
;;
|
|
*.toc)
|
|
# We only care about processing project TOC files
|
|
if [[ -n ${toc_interfaces["$_cdt_source_file"]} ]]; then
|
|
# Set the interface values
|
|
_cdt_toc_dir="${_cdt_source_file%/*}"
|
|
set_info_toc_interface "$_cdt_source_file" "${toc_root_paths["$_cdt_toc_dir"]}"
|
|
# Add build type filters
|
|
_cdt_filters+="|do_not_package_filter toc"
|
|
[ -n "$_cdt_nolib" ] && _cdt_filters+="|toc_filter no-lib-strip true" # leave the tokens in the file normally
|
|
_cdt_filters+="|toc_filter debug ${_cdt_debug}"
|
|
_cdt_filters+="|toc_filter alpha ${_cdt_alpha}"
|
|
# Add game type filters, skipping if multi-version
|
|
if [[ -z $_cdt_file_gametype && -n $_cdt_split && -n $si_game_root_interface ]] && ! is_toc_multiple_types "$si_game_root_interface" ; then
|
|
toc_to_type "$si_game_root_interface" "_cdt_file_gametype"
|
|
fi
|
|
if [[ -n "$_cdt_file_gametype" ]]; then
|
|
_cdt_filters+="|toc_filter retail $([[ "$_cdt_file_gametype" != "retail" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-retail $([[ "$_cdt_file_gametype" != "retail" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-classic $([[ "$_cdt_file_gametype" != "classic" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-vanilla $([[ "$_cdt_file_gametype" != "classic" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-bcc $([[ "$_cdt_file_gametype" != "bcc" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-wrath $([[ "$_cdt_file_gametype" != "wrath" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-cata $([[ "$_cdt_file_gametype" != "cata" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-mists $([[ "$_cdt_file_gametype" != "mists" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-titan $([[ "$_cdt_file_gametype" != "titan" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-forever $([[ "$_cdt_file_gametype" != "forever" ]] && echo "true")"
|
|
fi
|
|
# Rewrite the interface line if necessary
|
|
_cdt_filters+="|toc_interface_filter '${si_game_type_interface_all[${_cdt_file_gametype:- }]}' '${toc_root_interface["$_cdt_source_file"]}'"
|
|
# Do localized key replacements (this is really inefficient and should probably be deprecated)
|
|
[ -n "$_cdt_localization" ] && grep -q "@localization" "$_cdt_source_file" && _cdt_filters+="|localization_filter"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Set the filter for normalizing line endings.
|
|
_cdt_filters+="|line_ending_filter"
|
|
|
|
# Set version control values for the file.
|
|
set_info_file "$_cdt_source_file"
|
|
|
|
echo " Copying: $file${_cdt_external_slug:+ (embedded: "$_cdt_external_slug")}"
|
|
|
|
# Make sure we're not causing any surprises
|
|
if [[ -z $_cdt_file_gametype && ( $file == *".lua" || $file == *".xml" || ( -z $_cdt_external && $file == *".toc" ) ) ]] && grep -q '@\(non-\)\?version-\(retail\|classic\|vanilla\|bcc\|wrath\|cata\|mists\|titan\|forever\)@' "$_cdt_source_file"; then
|
|
echo " Error! Build type version keywords are not allowed in a multi-version build." >&2
|
|
echo " These should be replaced with lua conditional statements and/or TOC loading conditions:" >&2
|
|
grep -n '@\(non-\)\?version-\(retail\|classic\|vanilla\|bcc\|wrath\|cata\|mists\|titan\|forever\)@' "$_cdt_source_file" | sed 's/^/ /' >&2
|
|
echo " See https://warcraft.wiki.gg/wiki/WOW_PROJECT_ID" >&2
|
|
echo " See https://warcraft.wiki.gg/wiki/TOC_format" >&2
|
|
exit 1
|
|
fi
|
|
|
|
set -o pipefail
|
|
eval < "$_cdt_source_file" "$_cdt_filters" 3>&1 > "$_cdt_destdir/$file" || exit 1
|
|
|
|
# Create game type specific TOCs
|
|
if [[ -n $_cdt_split && -n ${toc_interfaces["$_cdt_source_file"]} ]]; then
|
|
local toc_version new_file
|
|
local root_toc_version="$si_game_root_interface"
|
|
for type in "${!si_game_type_interface[@]}"; do
|
|
toc_version="${si_game_type_interface[$type]}"
|
|
new_file="${file%.toc}"
|
|
case $type in
|
|
retail) new_file+="_Mainline.toc" ;;
|
|
classic) new_file+="_Vanilla.toc" ;;
|
|
bcc) new_file+="_TBC.toc" ;;
|
|
wrath) new_file+="_Wrath.toc" ;;
|
|
cata) new_file+="_Cata.toc" ;;
|
|
mists) new_file+="_Mists.toc" ;;
|
|
forever) new_file+="_Camelot.toc" ;;
|
|
# titan) new_file+="_Wrath.toc" ;;
|
|
esac
|
|
|
|
echo " Creating $new_file [${toc_version//:/, }]"
|
|
|
|
_cdt_filters="vcs_filter"
|
|
_cdt_filters+="|do_not_package_filter toc"
|
|
[ -n "$_cdt_nolib" ] && _cdt_filters+="|toc_filter no-lib-strip true" # leave the tokens in the file normally
|
|
_cdt_filters+="|toc_filter debug true"
|
|
_cdt_filters+="|toc_filter alpha ${_cdt_alpha}"
|
|
_cdt_filters+="|toc_filter retail $([[ "$type" != "retail" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-retail $([[ "$type" != "retail" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-classic $([[ "$type" != "classic" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-vanilla $([[ "$type" != "classic" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-bcc $([[ "$type" != "bcc" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-wrath $([[ "$type" != "wrath" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-cata $([[ "$type" != "cata" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-mists $([[ "$type" != "mists" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-titan $([[ "$type" != "titan" ]] && echo "true")"
|
|
_cdt_filters+="|toc_filter version-forever $([[ "$type" != "forever" ]] && echo "true")"
|
|
_cdt_filters+="|toc_interface_filter '$toc_version' '$root_toc_version'"
|
|
_cdt_filters+="|line_ending_filter"
|
|
|
|
eval < "$_cdt_source_file" "$_cdt_filters" 3>&1 > "$_cdt_destdir/$new_file"
|
|
done
|
|
|
|
if [[ -z $root_toc_version ]]; then
|
|
echo " Removing $file (no base interface)"
|
|
rm -f "$_cdt_destdir/$file"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
done < <( cd "$_cdt_srcdir" && eval "$_cdt_find_cmd" )
|
|
if [ -z "$_external_dir" ]; then
|
|
end_group "copy"
|
|
fi
|
|
}
|
|
|
|
if [ -z "$skip_copying" ]; then
|
|
cdt_args="-dp"
|
|
[ "$file_type" != "alpha" ] && cdt_args+="a"
|
|
[ -z "$skip_localization" ] && cdt_args+="l"
|
|
[ -n "$nolib" ] && cdt_args+="n"
|
|
[ -n "$split" ] && cdt_args+="S"
|
|
[ -n "$game_type" ] && cdt_args+=" -g $game_type"
|
|
[ -n "$ignore" ] && cdt_args+=" -i \"$ignore\""
|
|
[ -n "$unchanged" ] && cdt_args+=" -u \"$unchanged\""
|
|
eval copy_directory_tree "$cdt_args" "\"$topdir\"" "\"$pkgdir\""
|
|
fi
|
|
|
|
# Reset ignore and parse pkgmeta ignores again to handle ignoring external paths
|
|
ignore=
|
|
unchanged=
|
|
parse_ignore "$pkgmeta_file"
|
|
|
|
###
|
|
### Process .pkgmeta again to perform any pre-move-folders actions.
|
|
###
|
|
|
|
# Checkout the external into a ".checkout" subdirectory of the final directory.
|
|
checkout_external() {
|
|
local _external_dir="$1"
|
|
local _external_uri="$2"
|
|
local _external_tag="$3"
|
|
local _external_type="$4"
|
|
# shellcheck disable=SC2034 # _external_slug unused until we can easily fetch the project id
|
|
local _external_slug="$5"
|
|
local _external_checkout_type="$6"
|
|
local _external_path="$7"
|
|
|
|
_cqe_checkout_dir="$pkgdir/$_external_dir/.checkout"
|
|
if [[ -d $_cqe_checkout_dir ]]; then
|
|
# cleanup in case there was an aborted attempt to checkout
|
|
rm -rf "$_cqe_checkout_dir"
|
|
fi
|
|
mkdir -p "$_cqe_checkout_dir"
|
|
# The .pkgmeta to read "ignore" and "plain-copy" from, and the path its patterns are matched in.
|
|
local _cqe_pkgmeta="$_cqe_checkout_dir/.pkgmeta"
|
|
local _cqe_pkgmeta_path="$_external_path"
|
|
if [ "$_external_type" = "git" ]; then
|
|
if [ -z "$_external_tag" ]; then
|
|
echo "Fetching latest version of external $_external_uri"
|
|
retry git clone -q --depth 1 "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
elif [ "$_external_tag" != "latest" ]; then
|
|
echo "Fetching $_external_checkout_type \"$_external_tag\" from external $_external_uri"
|
|
if [ "$_external_checkout_type" = "commit" ]; then
|
|
retry git clone -q "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
git -C "$_cqe_checkout_dir" checkout -q "$_external_tag" || return 1
|
|
else
|
|
git -c advice.detachedHead=false clone -q --depth 1 --branch "$_external_tag" "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
fi
|
|
else # [ "$_external_tag" = "latest" ]; then
|
|
retry git clone -q --depth 50 "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
_external_tag=$( git -C "$_cqe_checkout_dir" for-each-ref refs/tags --sort=-creatordate --format=%\(refname:short\) --count=1 )
|
|
if [ -n "$_external_tag" ]; then
|
|
echo "Fetching tag \"$_external_tag\" from external $_external_uri"
|
|
git -C "$_cqe_checkout_dir" checkout -q "$_external_tag" || return 1
|
|
else
|
|
echo "Fetching latest version of external $_external_uri"
|
|
fi
|
|
fi
|
|
|
|
# pull submodules
|
|
git -C "$_cqe_checkout_dir" submodule -q update --init --recursive || return 1
|
|
|
|
set_info_git "$_cqe_checkout_dir"
|
|
echo "Checked out $( git -C "$_cqe_checkout_dir" describe --always --tags --abbrev=7 --long )" #$si_project_abbreviated_hash
|
|
elif [ "$_external_type" = "svn" ]; then
|
|
if [[ $external_uri == *"/trunk" ]]; then
|
|
_cqe_svn_trunk_url=$_external_uri
|
|
_cqe_svn_subdir=
|
|
else
|
|
_cqe_svn_trunk_url="${_external_uri%/trunk/*}/trunk"
|
|
_cqe_svn_subdir=${_external_uri#${_cqe_svn_trunk_url}/}
|
|
fi
|
|
_cqe_svn_root_url=$_cqe_svn_trunk_url
|
|
|
|
if [ -z "$_external_tag" ]; then
|
|
echo "Fetching latest version of external $_external_uri"
|
|
retry_svn_checkout "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
else
|
|
_cqe_svn_tag_url="${_cqe_svn_trunk_url%/trunk}/tags"
|
|
if [ "$_external_tag" = "latest" ]; then
|
|
_external_tag=$( retry svn log --verbose --limit 1 "$_cqe_svn_tag_url" 2>/dev/null | awk '/^ A \/tags\// { print $2; exit }' | awk -F/ '{ print $3 }' )
|
|
if [ -z "$_external_tag" ]; then
|
|
_external_tag="latest"
|
|
fi
|
|
fi
|
|
if [ "$_external_tag" = "latest" ]; then
|
|
echo "No tags found in $_cqe_svn_tag_url"
|
|
echo "Fetching latest version of external $_external_uri"
|
|
retry_svn_checkout "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
else
|
|
_cqe_external_uri="${_cqe_svn_tag_url}/$_external_tag"
|
|
_cqe_svn_root_url=$_cqe_external_uri
|
|
if [ -n "$_cqe_svn_subdir" ]; then
|
|
_cqe_external_uri="${_cqe_external_uri}/$_cqe_svn_subdir"
|
|
fi
|
|
echo "Fetching tag \"$_external_tag\" from external $_cqe_external_uri"
|
|
retry_svn_checkout "$_cqe_external_uri" "$_cqe_checkout_dir" || return 1
|
|
fi
|
|
fi
|
|
set_info_svn "$_cqe_checkout_dir" || return 1
|
|
echo "Checked out r$si_project_revision"
|
|
|
|
# A checkout of a subdirectory leaves the .pkgmeta in the root behind, so fetch it.
|
|
if [[ $_external_uri == *"/trunk/"* && ! -f "$_cqe_pkgmeta" ]]; then
|
|
retry svn_cat_optional "$_cqe_svn_root_url/.pkgmeta" "$_cqe_pkgmeta" || return 1
|
|
_cqe_pkgmeta_path="$_cqe_svn_subdir${_external_path:+/$_external_path}"
|
|
fi
|
|
elif [ "$_external_type" = "hg" ]; then
|
|
if [ -z "$_external_tag" ]; then
|
|
echo "Fetching latest version of external $_external_uri"
|
|
retry hg clone -q "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
elif [ "$_external_tag" != "latest" ]; then
|
|
echo "Fetching $_external_checkout_type \"$_external_tag\" from external $_external_uri"
|
|
retry hg clone -q --updaterev "$_external_tag" "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
else # [ "$_external_tag" = "latest" ]; then
|
|
retry hg clone -q "$_external_uri" "$_cqe_checkout_dir" || return 1
|
|
_external_tag=$( hg --cwd "$_cqe_checkout_dir" log -r . --template '{latesttag}' )
|
|
if [ -n "$_external_tag" ]; then
|
|
echo "Fetching tag \"$_external_tag\" from external $_external_uri"
|
|
hg --cwd "$_cqe_checkout_dir" update -q "$_external_tag"
|
|
else
|
|
echo "Fetching latest version of external $_external_uri"
|
|
fi
|
|
fi
|
|
set_info_hg "$_cqe_checkout_dir"
|
|
echo "Checked out r$si_project_revision"
|
|
elif [ "$_external_type" = "zip" ]; then
|
|
echo "Fetching archive $_external_uri"
|
|
local _cqe_zip_file="$_cqe_checkout_dir.zip"
|
|
local _cqe_zip_result=0
|
|
local _cqe_zip_root
|
|
if curl -sSfL --retry 3 --retry-delay 10 -o "$_cqe_zip_file" "$_external_uri"; then
|
|
unzip -qq "$_cqe_zip_file" -d "$_cqe_checkout_dir" || _cqe_zip_result=$?
|
|
# unzip exits with 1 for a warning, where everything was still extracted
|
|
[ "$_cqe_zip_result" -eq 1 ] && _cqe_zip_result=0
|
|
else
|
|
_cqe_zip_result=$?
|
|
fi
|
|
rm -f "$_cqe_zip_file"
|
|
[ "$_cqe_zip_result" -eq 0 ] || return 1
|
|
|
|
# Use a lone top-level directory as the root, like the one a GitHub archive is wrapped in.
|
|
_cqe_zip_root=$( find "$_cqe_checkout_dir" -mindepth 1 -maxdepth 1 )
|
|
if [ -d "$_cqe_zip_root" ]; then
|
|
_external_path="${_cqe_zip_root##*/}${_external_path:+/$_external_path}"
|
|
fi
|
|
|
|
# An archive is used as it is, so copy all of its files without processing them.
|
|
_cqe_pkgmeta=
|
|
unchanged="${unchanged:+$unchanged:}$_external_dir/*"
|
|
echo "Extracted $( find "$_cqe_checkout_dir" -type f | wc -l | tr -d ' ' ) files"
|
|
else
|
|
echo "Unknown external: $_external_uri" >&2
|
|
return 1
|
|
fi
|
|
# Copy the checkout into the proper external directory.
|
|
(
|
|
cd "$_cqe_checkout_dir" || return 1
|
|
# Set the slug for external localization, if needed.
|
|
# Note: We don't actually do localization since we need the project id and
|
|
# the only way to convert slug->id would be to scrape the project page :\
|
|
slug= #$_external_slug
|
|
project_site=
|
|
package=
|
|
if [[ "$_external_uri" == *"wowace.com"* || "$_external_uri" == *"curseforge.com"* ]]; then
|
|
project_site="https://wow.curseforge.com"
|
|
fi
|
|
|
|
# If a .pkgmeta file is present, process it for "ignore" and "plain-copy" lists.
|
|
parse_ignore "$_cqe_pkgmeta" "$_external_dir" "$_cqe_pkgmeta_path"
|
|
if [ -n "$_external_path" ]; then
|
|
echo "Changing to /$_external_path"
|
|
_cqe_checkout_dir="$_cqe_checkout_dir/$_external_path"
|
|
if [ ! -d "$_cqe_checkout_dir" ]; then
|
|
echo " ERROR! Root directory set by \"path\" does not exist." >&2
|
|
return 1
|
|
fi
|
|
cd "$_cqe_checkout_dir" || return 1
|
|
fi
|
|
copy_directory_tree -dnpe -i "$ignore" -u "$unchanged" "$_cqe_checkout_dir" "$pkgdir/$_external_dir"
|
|
) || return 1
|
|
# Remove the ".checkout" subdirectory containing the full checkout.
|
|
if [ -d "$_cqe_checkout_dir" ]; then
|
|
rm -fr "$_cqe_checkout_dir"
|
|
fi
|
|
}
|
|
|
|
external_pids=()
|
|
|
|
external_dir=
|
|
external_uri=
|
|
external_tag=
|
|
external_type=
|
|
external_slug=
|
|
external_checkout_type=
|
|
external_path=
|
|
process_external() {
|
|
if [ -n "$external_dir" ] && [ -n "$external_uri" ] && [ -z "$skip_externals" ]; then
|
|
echo "Fetching external: $external_dir"
|
|
|
|
external_uri=${external_uri%%#*} # strip trailing comment
|
|
external_uri=${external_uri% *} # strip trailing space
|
|
external_uri=${external_uri%/} # strip trailing slash
|
|
|
|
# convert old curse repo urls
|
|
case $external_uri in
|
|
*git.curseforge.com*|*git.wowace.com*)
|
|
external_type="git"
|
|
# git://git.curseforge.com/wow/$slug/mainline.git -> https://repos.curseforge.com/wow/$slug
|
|
external_uri=${external_uri%/mainline.git}
|
|
external_uri="https://repos${external_uri#*://git}"
|
|
;;
|
|
*svn.curseforge.com*|*svn.wowace.com*)
|
|
external_type="svn"
|
|
# svn://svn.curseforge.com/wow/$slug/mainline/trunk -> https://repos.curseforge.com/wow/$slug/trunk
|
|
external_uri=${external_uri/\/mainline/}
|
|
external_uri="https://repos${external_uri#*://svn}"
|
|
;;
|
|
*hg.curseforge.com*|*hg.wowace.com*)
|
|
external_type="hg"
|
|
# http://hg.curseforge.com/wow/$slug/mainline -> https://repos.curseforge.com/wow/$slug
|
|
external_uri=${external_uri%/mainline}
|
|
external_uri="https://repos${external_uri#*://hg}"
|
|
;;
|
|
svn:*)
|
|
# just in case
|
|
external_type="svn"
|
|
;;
|
|
*.zip|*.zip\?*)
|
|
if [ -z "$external_type" ]; then
|
|
external_type="zip"
|
|
fi
|
|
;;
|
|
*)
|
|
if [ -z "$external_type" ]; then
|
|
external_type="git"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [[ $external_uri == "https://repos.curseforge.com/wow/"* || $external_uri == "https://repos.wowace.com/wow/"* ]]; then
|
|
if [ -z "$external_slug" ]; then
|
|
external_slug=${external_uri#*/wow/}
|
|
external_slug=${external_slug%%/*}
|
|
fi
|
|
|
|
# check if the repo is svn
|
|
local _svn_path=${external_uri#*/wow/$external_slug/}
|
|
if [[ "$_svn_path" == "trunk"* ]]; then
|
|
external_type="svn"
|
|
elif [[ "$_svn_path" == "tags/"* ]]; then
|
|
external_type="svn"
|
|
# change the tag path into the trunk path and use the tag var so it gets logged as a tag
|
|
external_tag=${_svn_path#tags/}
|
|
external_tag=${external_tag%%/*}
|
|
external_uri="${external_uri%/tags*}/trunk${_svn_path#tags/$external_tag}"
|
|
fi
|
|
fi
|
|
|
|
if [[ $external_type == "git" ]]; then
|
|
if ! command -v git &>/dev/null; then
|
|
echo " ERROR! \"$external_uri\" is a git repository, but git is not available." >&2
|
|
exit 1
|
|
fi
|
|
# check for subpath in urls we know the structure of
|
|
if [[ -n $external_slug && $external_uri == *"$external_slug/"* ]]; then
|
|
# CF: https://repos.curseforge.com/wow/libdothings-1-0/LibDoThings-1.0
|
|
external_path=${external_uri#*/wow/$external_slug/}
|
|
external_uri=${external_uri%/$external_path*}
|
|
elif [[ $external_uri == "https://github.com/"*/*/* ]]; then
|
|
# GitHub: https://github.com/nebularg/LibDoThings-1.0.git/LibDoThings-1.0
|
|
external_path=${external_uri#*.com/*/*/}
|
|
external_uri=${external_uri%/$external_path*}
|
|
fi
|
|
elif [[ $external_type == "svn" ]]; then
|
|
if ! command -v svn &>/dev/null; then
|
|
echo " ERROR! \"$external_uri\" is a subversion repository, but svn is not available." >&2
|
|
exit 1
|
|
fi
|
|
elif [[ $external_type == "hg" ]]; then
|
|
if ! command -v hg &>/dev/null; then
|
|
echo " ERROR! \"$external_uri\" is a mercurial repository, but hg is not available." >&2
|
|
exit 1
|
|
fi
|
|
elif [[ $external_type == "zip" ]]; then
|
|
if ! command -v unzip &>/dev/null; then
|
|
echo " ERROR! \"$external_uri\" is a zip archive, but unzip is not available." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$external_slug" ]; then
|
|
relations["${external_slug,,}"]="embeddedLibrary"
|
|
fi
|
|
|
|
checkout_external "$external_dir" "$external_uri" "$external_tag" "$external_type" "$external_slug" "$external_checkout_type" "$external_path" &> "$releasedir/.$BASHPID.externalout" &
|
|
external_pids+=($!)
|
|
fi
|
|
external_dir=
|
|
external_uri=
|
|
external_tag=
|
|
external_type=
|
|
external_slug=
|
|
external_checkout_type=
|
|
external_path=
|
|
}
|
|
|
|
# Don't leave extra files around if exited early
|
|
kill_externals() {
|
|
rm -f "$releasedir"/.*.externalout
|
|
kill 0
|
|
}
|
|
trap kill_externals INT
|
|
|
|
if [ -z "$skip_externals" ] && [ -f "$pkgmeta_file" ]; then
|
|
yaml_eof=
|
|
while [ -z "$yaml_eof" ]; do
|
|
IFS='' read -r yaml_line || yaml_eof="true"
|
|
# Skip commented out lines.
|
|
if [[ $yaml_line =~ ^[[:space:]]*\# ]]; then
|
|
continue
|
|
fi
|
|
# Strip any trailing CR character.
|
|
yaml_line="${yaml_line%"$carriage_return"}"
|
|
|
|
case $yaml_line in
|
|
[!\ ]*:*)
|
|
# Started a new section, so checkout any queued externals.
|
|
process_external
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
# Set the $pkgmeta_phase for stateful processing.
|
|
pkgmeta_phase="$yaml_key"
|
|
;;
|
|
" "*)
|
|
yaml_line="${yaml_line#"${yaml_line%%[! ]*}"}" # trim leading whitespace
|
|
case $yaml_line in
|
|
"- "*)
|
|
;;
|
|
*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
case $pkgmeta_phase in
|
|
externals)
|
|
case $yaml_key in
|
|
url) external_uri="$yaml_value" ;;
|
|
tag)
|
|
external_tag="$yaml_value"
|
|
external_checkout_type="$yaml_key"
|
|
;;
|
|
branch)
|
|
external_tag="$yaml_value"
|
|
external_checkout_type="$yaml_key"
|
|
;;
|
|
commit)
|
|
external_tag="$yaml_value"
|
|
external_checkout_type="$yaml_key"
|
|
;;
|
|
type) external_type="$yaml_value" ;;
|
|
curse-slug) external_slug="$yaml_value" ;;
|
|
path) external_path="$yaml_value" ;;
|
|
*)
|
|
# Started a new external, so checkout any queued externals.
|
|
process_external
|
|
|
|
external_dir="$yaml_key"
|
|
nolib_exclude="$nolib_exclude $pkgdir/$external_dir/*"
|
|
if [ -n "$yaml_value" ]; then
|
|
external_uri="$yaml_value"
|
|
# Immediately checkout this fully-specified external.
|
|
process_external
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done < "$pkgmeta_file"
|
|
# Reached end of file, so checkout any remaining queued externals.
|
|
process_external
|
|
|
|
if [ ${#external_pids[*]} -gt 0 ]; then
|
|
echo
|
|
echo "Waiting for externals to finish..."
|
|
echo
|
|
|
|
while [ ${#external_pids[*]} -gt 0 ]; do
|
|
wait -n
|
|
for i in ${!external_pids[*]}; do
|
|
pid=${external_pids[i]}
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
_external_output="$releasedir/.$pid.externalout"
|
|
if ! wait "$pid"; then
|
|
_external_error=1
|
|
# wrap each line with a bright red color code
|
|
awk '{ printf "\033[01;31m%s\033[0m\n", $0 }' "$_external_output"
|
|
echo
|
|
else
|
|
start_group "$( head -n1 "$_external_output" )" "external.$pid"
|
|
tail -n+2 "$_external_output"
|
|
end_group "external.$pid"
|
|
fi
|
|
rm -f "$_external_output" 2>/dev/null
|
|
unset 'external_pids[i]'
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ -n "$_external_error" ]; then
|
|
echo
|
|
echo "There was an error fetching externals :(" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Restore the signal handlers
|
|
trap - INT
|
|
|
|
###
|
|
### Create the changelog of commits since the previous release tag.
|
|
###
|
|
|
|
if [ -z "$project" ]; then
|
|
project="$package"
|
|
fi
|
|
|
|
# Create a changelog in the package directory if the source directory does
|
|
# not contain a manual changelog.
|
|
|
|
changelog_path=
|
|
if [[ -n "$manual_changelog" && -f "$topdir/$changelog" ]]; then
|
|
if [[ -f "$pkgdir/$changelog" ]]; then
|
|
# use the processed copy
|
|
changelog_path="$pkgdir/$changelog"
|
|
else
|
|
changelog_path="$topdir/$changelog"
|
|
fi
|
|
start_group "Using manual changelog $changelog" "changelog"
|
|
head -n7 "$changelog_path"
|
|
[ "$( wc -l < "$changelog_path" )" -gt 7 ] && echo "..."
|
|
end_group "changelog"
|
|
|
|
# Convert Markdown to BBCode (with HTML as an intermediary) for sending to WoWInterface
|
|
# Requires pandoc (http://pandoc.org/)
|
|
if [ "$changelog_markup" = "markdown" ] && [ -n "$wowi_convert_changelog" ] && command -v pandoc &>/dev/null; then
|
|
wowi_changelog="$releasedir/WOWI-$project_version-CHANGELOG.txt"
|
|
pandoc --wrap=none -f commonmark -t html "$changelog_path" | sed \
|
|
-e 's/<\(\/\)\?\(b\|i\|u\)>/[\1\2]/g' \
|
|
-e 's/<\(\/\)\?em>/[\1i]/g' \
|
|
-e 's/<\(\/\)\?strong>/[\1b]/g' \
|
|
-e 's/<ul[^>]*>/[list]/g' -e 's/<ol[^>]*>/[list="1"]/g' \
|
|
-e 's/<\/[ou]l>/[\/list]\n/g' \
|
|
-e 's/<li><p>/[*]/g' -e 's/<li>/[*]/g' -e 's/<\/p><\/li>//g' -e 's/<\/li>//g' \
|
|
-e 's/\[\*\]\[ \] /[*]☐ /g' -e 's/\[\*\]\[[xX]\] /[*]☒ /g' \
|
|
-e 's/<h1[^>]*>/[size="6"]/g' -e 's/<h2[^>]*>/[size="5"]/g' -e 's/<h3[^>]*>/[size="4"]/g' \
|
|
-e 's/<h4[^>]*>/[size="3"]/g' -e 's/<h5[^>]*>/[size="3"]/g' -e 's/<h6[^>]*>/[size="3"]/g' \
|
|
-e 's/<\/h[1-6]>/[\/size]\n/g' \
|
|
-e 's/<blockquote>/[quote]/g' -e 's/<\/blockquote>/[\/quote]\n/g' \
|
|
-e 's/<div class="sourceCode"[^>]*><pre class="sourceCode lua"><code class="sourceCode lua">/[highlight="lua"]/g' -e 's/<\/code><\/pre><\/div>/[\/highlight]\n/g' \
|
|
-e 's/<pre><code>/[code]/g' -e 's/<\/code><\/pre>/[\/code]\n/g' \
|
|
-e 's/<code>/[font="monospace"]/g' -e 's/<\/code>/[\/font]/g' \
|
|
-e 's/<a href=\"\([^"]\+\)\"[^>]*>/[url="\1"]/g' -e 's/<\/a>/\[\/url]/g' \
|
|
-e 's/<img src=\"\([^"]\+\)\"[^>]*>/[img]\1[\/img]/g' \
|
|
-e 's/<hr \/>/_____________________________________________________________________________\n/g' \
|
|
-e 's/<\/p>/\n/g' \
|
|
-e '/^<[^>]\+>$/d' -e 's/<[^>]\+>//g' \
|
|
-e 's/"/"/g' \
|
|
-e 's/&/&/g' \
|
|
-e 's/</</g' \
|
|
-e 's/>/>/g' \
|
|
-e "s/'/'/g" \
|
|
| line_ending_filter > "$wowi_changelog"
|
|
fi
|
|
else
|
|
if [ -n "$manual_changelog" ]; then
|
|
echo "Warning! Could not find a manual changelog at $topdir/$changelog"
|
|
manual_changelog=
|
|
fi
|
|
changelog="CHANGELOG.md"
|
|
changelog_markup="markdown"
|
|
changelog_path="$pkgdir/$changelog"
|
|
|
|
if [ -n "$wowi_gen_changelog" ] && [ -z "$wowi_convert_changelog" ]; then
|
|
wowi_markup="markdown"
|
|
fi
|
|
|
|
start_group "Generating changelog of commits into $changelog" "changelog"
|
|
|
|
_changelog_range=
|
|
if [ "$repository_type" = "git" ]; then
|
|
changelog_url=
|
|
changelog_version=
|
|
changelog_previous="[Previous Releases](${project_github_url}/releases)"
|
|
changelog_url_wowi=
|
|
changelog_version_wowi=
|
|
changelog_previous_wowi="[url=${project_github_url}/releases]Previous Releases[/url]"
|
|
if [ -z "$previous_version" ] && [ -z "$tag" ]; then
|
|
# no range, show all commits up to ours
|
|
changelog_url="[Full Changelog](${project_github_url}/commits/${project_hash})"
|
|
changelog_version="[${project_version}](${project_github_url}/tree/${project_hash})"
|
|
changelog_url_wowi="[url=${project_github_url}/commits/${project_hash}]Full Changelog[/url]"
|
|
changelog_version_wowi="[url=${project_github_url}/tree/${project_hash}]${project_version}[/url]"
|
|
_changelog_range="$project_hash"
|
|
elif [ -z "$previous_version" ] && [ -n "$tag" ]; then
|
|
# first tag, show all commits upto it
|
|
changelog_url="[Full Changelog](${project_github_url}/commits/${tag})"
|
|
changelog_version="[${project_version}](${project_github_url}/tree/${tag})"
|
|
changelog_url_wowi="[url=${project_github_url}/commits/${tag}]Full Changelog[/url]"
|
|
changelog_version_wowi="[url=${project_github_url}/tree/${tag}]${project_version}[/url]"
|
|
_changelog_range="$tag"
|
|
elif [ -n "$previous_version" ] && [ -z "$tag" ]; then
|
|
# compare between last tag and our commit
|
|
changelog_url="[Full Changelog](${project_github_url}/compare/${previous_version}...${project_hash})"
|
|
changelog_version="[$project_version](${project_github_url}/tree/${project_hash})"
|
|
changelog_url_wowi="[url=${project_github_url}/compare/${previous_version}...${project_hash}]Full Changelog[/url]"
|
|
changelog_version_wowi="[url=${project_github_url}/tree/${project_hash}]${project_version}[/url]"
|
|
_changelog_range="$previous_version..$project_hash"
|
|
elif [ -n "$previous_version" ] && [ -n "$tag" ]; then
|
|
# compare between last tag and our tag
|
|
changelog_url="[Full Changelog](${project_github_url}/compare/${previous_version}...${tag})"
|
|
changelog_version="[$project_version](${project_github_url}/tree/${tag})"
|
|
changelog_url_wowi="[url=${project_github_url}/compare/${previous_version}...${tag}]Full Changelog[/url]"
|
|
changelog_version_wowi="[url=${project_github_url}/tree/${tag}]${project_version}[/url]"
|
|
_changelog_range="$previous_version..$tag"
|
|
fi
|
|
# lazy way out
|
|
if [ -z "$project_github_url" ]; then
|
|
changelog_url=
|
|
changelog_version=$project_version
|
|
changelog_previous=
|
|
changelog_url_wowi=
|
|
changelog_version_wowi="[color=orange]${project_version}[/color]"
|
|
changelog_previous_wowi=
|
|
elif [ -z "$github_token" ]; then
|
|
# not creating releases :(
|
|
changelog_previous=
|
|
changelog_previous_wowi=
|
|
fi
|
|
changelog_date=$( TZ='' printf "%(%Y-%m-%d)T" "$project_timestamp" )
|
|
|
|
cat <<- EOF | line_ending_filter > "$changelog_path"
|
|
# $project
|
|
|
|
## $changelog_version ($changelog_date)
|
|
$changelog_url $changelog_previous
|
|
|
|
EOF
|
|
# shellcheck disable=SC2016 # don't expand backticks
|
|
git -C "$topdir" log "$_changelog_range" --pretty=format:"###%B" \
|
|
| sed -e 's/^/ /g' -e 's/^ *$//g' -e 's/^ ###/- /g' -e 's/$/ /' \
|
|
-e ':a;s/^\(\(`[^`]*`\|[^`_]*\)*\)_/\1\\###/;ta' -e 's/###/_/g' \
|
|
-e 's/\[ci skip\]//g' -e 's/\[skip ci\]//g' \
|
|
-e '/git-svn-id:/d' -e '/^[[:space:]]*This reverts commit [0-9a-f]\{40\}\.[[:space:]]*$/d' \
|
|
-e '/^[[:space:]]*$/d' \
|
|
| line_ending_filter >> "$changelog_path"
|
|
|
|
# WoWI uses BBCode, generate something usable to post to the site
|
|
# the file is deleted on successful upload
|
|
if [ -n "$addonid" ] && [ -n "$tag" ] && [ -n "$wowi_gen_changelog" ] && [ "$wowi_markup" = "bbcode" ]; then
|
|
wowi_changelog="$releasedir/WOWI-$project_version-CHANGELOG.txt"
|
|
cat <<- EOF | line_ending_filter > "$wowi_changelog"
|
|
[size=5]${project}[/size]
|
|
[size=4]${changelog_version_wowi} (${changelog_date})[/size]
|
|
${changelog_url_wowi} ${changelog_previous_wowi}
|
|
[list]
|
|
EOF
|
|
git -C "$topdir" log "$_changelog_range" --pretty=format:"###%B" \
|
|
| sed -e 's/^/ /g' -e 's/^ *$//g' -e 's/^ ###/[*]/g' \
|
|
-e 's/\[ci skip\]//g' -e 's/\[skip ci\]//g' \
|
|
-e '/git-svn-id:/d' -e '/^[[:space:]]*This reverts commit [0-9a-f]\{40\}\.[[:space:]]*$/d' \
|
|
-e '/^[[:space:]]*$/d' \
|
|
| line_ending_filter >> "$wowi_changelog"
|
|
echo "[/list]" | line_ending_filter >> "$wowi_changelog"
|
|
fi
|
|
|
|
elif [ "$repository_type" = "svn" ]; then
|
|
if [ -n "$previous_revision" ]; then
|
|
_changelog_range="-r$project_revision:$previous_revision"
|
|
else
|
|
_changelog_range="-rHEAD:1"
|
|
fi
|
|
changelog_date=$( TZ='' printf "%(%Y-%m-%d)T" "$project_timestamp" )
|
|
|
|
cat <<- EOF | line_ending_filter > "$changelog_path"
|
|
# $project
|
|
|
|
## $project_version ($changelog_date)
|
|
|
|
EOF
|
|
_svn_changelog=$( retry svn log "$topdir" "$_changelog_range" --xml )
|
|
# shellcheck disable=SC2016 # don't expand backticks
|
|
echo "$_svn_changelog" \
|
|
| awk '/<msg>/,/<\/msg>/' \
|
|
| sed -e 's/<msg>/###/g' -e 's/<\/msg>//g' \
|
|
-e 's/^/ /g' -e 's/^ *$//g' -e 's/^ ###/- /g' -e 's/$/ /' \
|
|
-e ':a;s/^\(\(`[^`]*`\|[^`_]*\)*\)_/\1\\###/;ta' -e 's/###/_/g' \
|
|
-e 's/\[ci skip\]//g' -e 's/\[skip ci\]//g' \
|
|
-e '/^[[:space:]]*$/d' \
|
|
| line_ending_filter >> "$changelog_path"
|
|
|
|
# WoWI uses BBCode, generate something usable to post to the site
|
|
# the file is deleted on successful upload
|
|
if [ -n "$addonid" ] && [ -n "$tag" ] && [ -n "$wowi_gen_changelog" ] && [ "$wowi_markup" = "bbcode" ]; then
|
|
wowi_changelog="$releasedir/WOWI-$project_version-CHANGELOG.txt"
|
|
cat <<- EOF | line_ending_filter > "$wowi_changelog"
|
|
[size=5]${project}[/size]
|
|
[size=4][color=orange]${project_version}[/color] (${changelog_date})[/size]
|
|
|
|
[list]
|
|
EOF
|
|
echo "$_svn_changelog" \
|
|
| awk '/<msg>/,/<\/msg>/' \
|
|
| sed -e 's/<msg>/###/g' -e 's/<\/msg>//g' \
|
|
-e 's/^/ /g' -e 's/^ *$//g' -e 's/^ ###/[*]/g' \
|
|
-e 's/\[ci skip\]//g' -e 's/\[skip ci\]//g' \
|
|
-e '/^[[:space:]]*$/d' \
|
|
| line_ending_filter >> "$wowi_changelog"
|
|
echo "[/list]" | line_ending_filter >> "$wowi_changelog"
|
|
fi
|
|
unset _svn_changelog
|
|
|
|
elif [ "$repository_type" = "hg" ]; then
|
|
if [ -n "$previous_revision" ]; then
|
|
_changelog_range="::$project_revision - ::$previous_revision - filelog(.hgtags)"
|
|
else
|
|
_changelog_range="."
|
|
fi
|
|
changelog_date=$( TZ='' printf "%(%Y-%m-%d)T" "$project_timestamp" )
|
|
|
|
cat <<- EOF | line_ending_filter > "$changelog_path"
|
|
# $project
|
|
|
|
## $project_version ($changelog_date)
|
|
|
|
EOF
|
|
hg --cwd "$topdir" log -r "$_changelog_range" --template '- {fill(desc|strip, 76, "", " ")}\n' | line_ending_filter >> "$changelog_path"
|
|
|
|
# WoWI uses BBCode, generate something usable to post to the site
|
|
# the file is deleted on successful upload
|
|
if [ -n "$addonid" ] && [ -n "$tag" ] && [ -n "$wowi_gen_changelog" ] && [ "$wowi_markup" = "bbcode" ]; then
|
|
wowi_changelog="$releasedir/WOWI-$project_version-CHANGELOG.txt"
|
|
cat <<- EOF | line_ending_filter > "$wowi_changelog"
|
|
[size=5]${project}[/size]
|
|
[size=4][color=orange]${project_version}[/color] (${changelog_date})[/size]
|
|
|
|
[list]
|
|
EOF
|
|
hg --cwd "$topdir" log "$_changelog_range" --template '[*]{desc|strip|escape}\n' | line_ending_filter >> "$wowi_changelog"
|
|
echo "[/list]" | line_ending_filter >> "$wowi_changelog"
|
|
fi
|
|
fi
|
|
|
|
echo "$(<"$changelog_path")"
|
|
end_group "changelog"
|
|
fi
|
|
|
|
###
|
|
### Create a license if not present and .pkgmeta requests one.
|
|
###
|
|
|
|
if [[ -n "$license" && ! -f "$topdir/$license" && -n "$slug" ]]; then
|
|
start_group "Saving license as $license" "license"
|
|
# this only exists on wowace.com now
|
|
if license_text=$( curl -sf --retry 3 --retry-delay 10 "https://www.wowace.com/project/$slug/license" 2>/dev/null ); then
|
|
# text is wrapped with \n\n<div class="module">\n\t<p>\n\t\t ... \n\t</p>\n</div>\n
|
|
echo "$license_text" | sed -e '1,4d' -e '5s/^\s*//' -e '$d' | sed '$d' > "$pkgdir/$license"
|
|
head -n10 "$pkgdir/$license"
|
|
[[ "$( wc -l < "$pkgdir/$license" )" -gt 10 ]] && echo "..."
|
|
else
|
|
echo "There was an error saving the license. ($?)"
|
|
fi
|
|
end_group "license"
|
|
fi
|
|
|
|
###
|
|
### Process .pkgmeta to perform move-folders actions.
|
|
###
|
|
|
|
if [ -f "$pkgmeta_file" ]; then
|
|
yaml_eof=
|
|
while [ -z "$yaml_eof" ]; do
|
|
IFS='' read -r yaml_line || yaml_eof="true"
|
|
# Skip commented out lines.
|
|
if [[ $yaml_line =~ ^[[:space:]]*\# ]]; then
|
|
continue
|
|
fi
|
|
# Strip any trailing CR character.
|
|
yaml_line=${yaml_line%$carriage_return}
|
|
|
|
case $yaml_line in
|
|
[!\ ]*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
# Set the $pkgmeta_phase for stateful processing.
|
|
pkgmeta_phase=$yaml_key
|
|
;;
|
|
" "*)
|
|
yaml_line=${yaml_line#"${yaml_line%%[! ]*}"} # trim leading whitespace
|
|
case $yaml_line in
|
|
"- "*)
|
|
;;
|
|
*:*)
|
|
# Split $yaml_line into a $yaml_key, $yaml_value pair.
|
|
yaml_keyvalue "$yaml_line"
|
|
case $pkgmeta_phase in
|
|
move-folders)
|
|
srcdir="$releasedir/$yaml_key"
|
|
destdir="$releasedir/$yaml_value"
|
|
if [[ -d "$destdir" && -z "$overwrite" && "$srcdir" != "$destdir/"* ]]; then
|
|
rm -fr "$destdir"
|
|
fi
|
|
if [ -d "$srcdir" ]; then
|
|
if [ ! -d "$destdir" ]; then
|
|
mkdir -p "$destdir"
|
|
fi
|
|
echo "Moving $yaml_key to $yaml_value"
|
|
mv -f "$srcdir"/* "$destdir" && rm -fr "$srcdir"
|
|
zip_root_dirs+=("$yaml_value")
|
|
# Copy the license into $destdir if one doesn't already exist.
|
|
if [[ -n "$license" && -f "$pkgdir/$license" && ! -f "$destdir/$license" ]]; then
|
|
cp -f "$pkgdir/$license" "$destdir/$license"
|
|
fi
|
|
# Check to see if the base source directory is empty
|
|
_mf_basedir="/${yaml_key%/*}"
|
|
while [[ -n "$_mf_basedir" && -z "$( ls -A "${releasedir}${_mf_basedir}" )" ]]; do
|
|
echo "Removing empty directory ${_mf_basedir#/}"
|
|
rm -fr "${releasedir}${_mf_basedir}"
|
|
_mf_basedir="${_mf_basedir%/*}"
|
|
done
|
|
fi
|
|
# update external dir
|
|
nolib_exclude=${nolib_exclude//$srcdir/$destdir}
|
|
# update changelog path
|
|
if [[ "$changelog_path" == "$srcdir/"* && -f "$destdir/${changelog_path#"$srcdir/"}" ]]; then
|
|
changelog_path="$destdir/${changelog_path#"$srcdir/"}"
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done < "$pkgmeta_file"
|
|
if [ -n "$srcdir" ]; then
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
###
|
|
### Create the final zipfile for the addon.
|
|
###
|
|
|
|
if [ -z "$skip_zipfile" ]; then
|
|
archive_version="$project_version" # XXX used for wowi version. should probably switch to label, but the game type gets added on by default :\
|
|
archive_label="$( filename_filter "$label_template" )"
|
|
archive_name="$( filename_filter "$file_template" ).zip"
|
|
archive="$releasedir/$archive_name"
|
|
|
|
nolib_archive_version="${project_version}-nolib"
|
|
nolib_archive_label="$( nolib=true filename_filter "$archive_label" )"
|
|
nolib_archive_name="$( nolib=true filename_filter "$file_template" ).zip"
|
|
# someone didn't include {nolib} and they're forcing nolib creation
|
|
if [ "$archive_label" = "$nolib_archive_label" ]; then
|
|
nolib_archive_label="${nolib_archive_label}-nolib"
|
|
fi
|
|
if [ "$archive_name" = "$nolib_archive_name" ]; then
|
|
nolib_archive_name="${nolib_archive_name%.zip}-nolib.zip"
|
|
fi
|
|
nolib_archive="$releasedir/$nolib_archive_name"
|
|
|
|
if [ -n "$nolib" ]; then
|
|
archive_version="$nolib_archive_version"
|
|
archive_label="$nolib_archive_label"
|
|
archive_name="$nolib_archive_name"
|
|
archive="$nolib_archive"
|
|
nolib_archive=
|
|
fi
|
|
|
|
if [[ -n $GITHUB_ACTIONS ]]; then
|
|
echo "archive_path=${archive}" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
start_group "Creating archive: $archive_name ($archive_label)" "archive"
|
|
if [ -f "$archive" ]; then
|
|
rm -f "$archive"
|
|
fi
|
|
( cd "$releasedir" && zip -X -r "$archive" "${zip_root_dirs[@]}" )
|
|
|
|
if [ ! -f "$archive" ]; then
|
|
exit 1
|
|
fi
|
|
end_group "archive"
|
|
|
|
# Create nolib version of the zipfile
|
|
if [ -n "$enable_nolib_creation" ] && [ -z "$nolib" ] && [ -n "$nolib_exclude" ]; then
|
|
# run the nolib_filter
|
|
find "$pkgdir" -type f \( -name "*.xml" -o -name "*.toc" \) -print | while read -r file; do
|
|
case $file in
|
|
*.toc) _filter="toc_filter no-lib-strip true" ;;
|
|
*.xml) _filter="xml_filter no-lib-strip" ;;
|
|
esac
|
|
$_filter < "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
|
done
|
|
|
|
# make the exclude paths relative to the release directory
|
|
nolib_exclude=${nolib_exclude//$releasedir\//}
|
|
|
|
start_group "Creating no-lib archive: $nolib_archive_name ($nolib_archive_label)" "archive.nolib"
|
|
if [ -f "$nolib_archive" ]; then
|
|
rm -f "$nolib_archive"
|
|
fi
|
|
# shellcheck disable=SC2086 # set noglob so each nolib_exclude path gets quoted instead of expanded
|
|
( set -f; cd "$releasedir" && zip -X -r -q "$nolib_archive" "${zip_root_dirs[@]}" -x $nolib_exclude )
|
|
|
|
if [ ! -f "$nolib_archive" ]; then
|
|
exit_code=1
|
|
fi
|
|
end_group "archive.nolib"
|
|
fi
|
|
fi
|
|
|
|
###
|
|
### Deploy the zipfile.
|
|
###
|
|
|
|
# Upload to CurseForge.
|
|
upload_curseforge() {
|
|
if [[ -n "$skip_cf_upload" || -z "$slug" || -z "$cf_token" || -z "$project_site" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local _cf_game_version_id _cf_game_version _cf_versions
|
|
_cf_versions=$( curl -s -H "x-api-token: $cf_token" "$project_site/api/game/wow/versions" | jq -c '.' 2>/dev/null )
|
|
if [[ -n $_cf_versions && $_cf_versions != *"errorMessage"* ]]; then
|
|
_cf_game_version_id=
|
|
_cf_game_version=
|
|
local version_id game_id invalid_version
|
|
for type in "${!game_type_version[@]}"; do
|
|
case $type in
|
|
classic) game_id=67408 ;;
|
|
bcc) game_id=73246 ;;
|
|
wrath) game_id=73713 ;;
|
|
cata) game_id=77522 ;;
|
|
mists) game_id=79434 ;;
|
|
titan) game_id=81212 ;;
|
|
forever) game_id=88568 ;;
|
|
*) game_id=517
|
|
esac
|
|
IFS=':' read -ra V <<< "${game_type_version[$type]}"
|
|
for version in "${V[@]}"; do
|
|
# check the version
|
|
if ! jq -e --arg v "$version" --argjson t "$game_id" 'map(select(.gameVersionTypeID == $t and .name == $v)) | length > 0' <<< "$_cf_versions" &>/dev/null; then
|
|
invalid_version="$version"
|
|
# no match, so grab the next highest version (try to avoid testing versions)
|
|
version=$( echo "$_cf_versions" | jq -r --arg v "$invalid_version" --argjson t "$game_id" 'map(select(.gameVersionTypeID == $t and .name < $v)) | max_by(.id) | .name // empty' )
|
|
if [[ -z $version ]]; then
|
|
# no? just grab the highest version
|
|
version=$( echo "$_cf_versions" | jq -r --argjson t "$game_id" 'map(select(.gameVersionTypeID == $t)) | max_by(.id) | .name // empty' )
|
|
fi
|
|
if [[ -z $version ]]; then
|
|
echo "WARNING: No CurseForge game version match for \"$invalid_version\", ignoring" >&2
|
|
else
|
|
echo "WARNING: No CurseForge game version match for \"$invalid_version\", using \"$version\"" >&2
|
|
fi
|
|
fi
|
|
if [[ -n $version && "$_cf_game_version," != *",$version," ]]; then
|
|
_cf_game_version+=",${version}"
|
|
# get the id
|
|
version_id=$( echo "$_cf_versions" | jq -r --arg v "$version" --argjson t "$game_id" '.[] | select(.gameVersionTypeID == $t and .name == $v) | .id // empty' )
|
|
_cf_game_version_id+=",${version_id}"
|
|
fi
|
|
done
|
|
done
|
|
_cf_game_version_id="[${_cf_game_version_id#,}]"
|
|
_cf_game_version=$( echo "${_cf_game_version#,}" | tr ',' '\n' | sort -Vr | tr '\n' ',' )
|
|
_cf_game_version=${_cf_game_version%,}
|
|
fi
|
|
if [ -z "$_cf_game_version" ]; then
|
|
echo "Error fetching game version info from $project_site/api/game/versions"
|
|
echo
|
|
echo "Skipping upload to CurseForge."
|
|
echo
|
|
return 1
|
|
fi
|
|
|
|
local _cf_payload _cf_payload_relations
|
|
local resultfile result
|
|
local return_code=0
|
|
|
|
_cf_payload=$( cat <<-EOF
|
|
{
|
|
"displayName": "$archive_label",
|
|
"gameVersions": $_cf_game_version_id,
|
|
"releaseType": "$file_type",
|
|
"changelog": $( jq --slurp --raw-input '.' < "$changelog_path" ),
|
|
"changelogType": "$changelog_markup"
|
|
}
|
|
EOF
|
|
)
|
|
_cf_payload_relations=
|
|
for i in "${!relations[@]}"; do
|
|
_cf_payload_relations="$_cf_payload_relations{\"slug\":\"$i\",\"type\":\"${relations[$i]}\"},"
|
|
done
|
|
if [[ -n $_cf_payload_relations ]]; then
|
|
_cf_payload_relations="{\"relations\":{\"projects\":[${_cf_payload_relations%,}]}}"
|
|
_cf_payload=$( echo "$_cf_payload $_cf_payload_relations" | jq -s -c '.[0] * .[1]' )
|
|
fi
|
|
|
|
echo "Uploading $archive_name ($_cf_game_version $file_type) to https://wow.curseforge.com/projects/$slug"
|
|
resultfile="$releasedir/cf_result.json"
|
|
if result=$( echo "$_cf_payload" | curl -sS --retry 3 --retry-delay 10 \
|
|
-w "%{http_code}" -o "$resultfile" \
|
|
-H "x-api-token: $cf_token" \
|
|
-F "metadata=<-" \
|
|
-F "file=@$archive" \
|
|
"$project_site/api/projects/$slug/upload-file"
|
|
); then
|
|
case $result in
|
|
200) echo "Success!" ;;
|
|
302)
|
|
echo "Error! ($result)"
|
|
# don't need to ouput the redirect page
|
|
return_code=1
|
|
;;
|
|
404)
|
|
echo "Error! No project for \"$slug\" found."
|
|
return_code=1
|
|
;;
|
|
*)
|
|
echo "Error! ($result)"
|
|
if [ -s "$resultfile" ]; then
|
|
echo "$(<"$resultfile")"
|
|
fi
|
|
return_code=1
|
|
;;
|
|
esac
|
|
else
|
|
return_code=1
|
|
fi
|
|
echo
|
|
|
|
rm -f "$resultfile" 2>/dev/null
|
|
|
|
return "$return_code"
|
|
}
|
|
|
|
# Upload tags to WoWInterface.
|
|
upload_wowinterface() {
|
|
if [[ -z "$tag" || -z "$addonid" || -z "$wowi_token" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local _wowi_versions _wowi_game_version
|
|
_wowi_versions=$( curl -s https://api.wowinterface.com/addons/compatible.json | jq -c '.' 2>/dev/null )
|
|
if [ -n "$_wowi_versions" ]; then
|
|
local wowi_type invalid_version
|
|
for type in "${!game_type_version[@]}"; do
|
|
case $type in
|
|
classic) wowi_type="Classic" ;;
|
|
bcc) wowi_type="TBC-Classic" ;;
|
|
wrath) wowi_type="WOTLK-Classic" ;;
|
|
cata|mists) wowi_type="Cata-Classic" ;;
|
|
titan|forever)
|
|
echo "WARNING: No WoWInterface game type match for \"$type\", \"$type\" is not supported, ignoring" >&2
|
|
continue
|
|
;;
|
|
*) wowi_type="Retail"
|
|
esac
|
|
IFS=':' read -ra V <<< "${game_type_version[$type]}"
|
|
for version in "${V[@]}"; do
|
|
# check the version
|
|
if ! jq -e --arg v "$version" 'map(select(.id == $v)) | length > 0' <<< "$_wowi_versions" &>/dev/null; then
|
|
invalid_version="$version"
|
|
# use the next highest version (try to avoid testing versions)
|
|
version=$( echo "$_wowi_versions" | jq -r --arg v "$invalid_version" --arg t "$wowi_type" 'map(select(.game == $t and .id < $v)) | max_by(.id) | .id // empty' )
|
|
if [[ -z $version ]]; then
|
|
if [[ $type == "mists" ]]; then # XXX wowi dead, yo
|
|
wowi_type="Cata-Classic"
|
|
echo "WARNING: No WoWInterface game type match for \"$type\", using \"$wowi_type\"" >&2
|
|
fi
|
|
# just grab the highest version
|
|
version=$( echo "$_wowi_versions" | jq -r --arg t "$wowi_type" 'map(select(.game == $t)) | max_by(.id) | .id // empty' )
|
|
fi
|
|
if [[ -z $version ]]; then
|
|
echo "WARNING: No WoWInterface game version match for \"$invalid_version\", \"$wowi_type\" is not supported, ignoring" >&2
|
|
else
|
|
echo "WARNING: No WoWInterface game version match for \"$invalid_version\", using \"$version\"" >&2
|
|
fi
|
|
fi
|
|
if [[ -n $version && "$_wowi_game_version," != *",$version," ]]; then
|
|
_wowi_game_version+=",${version}"
|
|
fi
|
|
done
|
|
done
|
|
_wowi_game_version=$( echo "${_wowi_game_version#,}" | tr ',' '\n' | sort -Vr | tr '\n' ',' )
|
|
_wowi_game_version=${_wowi_game_version%,}
|
|
fi
|
|
if [ -z "$_wowi_game_version" ]; then
|
|
echo "Error fetching game version info from https://api.wowinterface.com/addons/compatible.json"
|
|
echo
|
|
echo "Skipping upload to WoWInterface."
|
|
echo
|
|
return 1
|
|
fi
|
|
|
|
local -a _wowi_args
|
|
local resultfile result
|
|
local return_code=0
|
|
|
|
if [ -f "$wowi_changelog" ]; then
|
|
_wowi_args+=("-F changelog=<$wowi_changelog")
|
|
elif [ -n "$manual_changelog" ] || [ "$wowi_markup" = "markdown" ]; then
|
|
_wowi_args+=("-F changelog=<$changelog_path")
|
|
fi
|
|
if [ -z "$wowi_archive" ]; then
|
|
_wowi_args+=("-F archive=No")
|
|
fi
|
|
|
|
echo "Uploading $archive_name ($_wowi_game_version) to https://www.wowinterface.com/downloads/info$addonid"
|
|
resultfile="$releasedir/wi_result.json"
|
|
if result=$( curl -sS --retry 3 --retry-delay 10 \
|
|
-w "%{http_code}" -o "$resultfile" \
|
|
-H "x-api-token: $wowi_token" \
|
|
-F "id=$addonid" \
|
|
-F "version=$archive_version" \
|
|
-F "compatible=$_wowi_game_version" \
|
|
"${_wowi_args[@]}" \
|
|
-F "updatefile=@$archive" \
|
|
"https://api.wowinterface.com/addons/update"
|
|
); then
|
|
case $result in
|
|
202)
|
|
echo "Success!"
|
|
if [ -f "$wowi_changelog" ]; then
|
|
rm -f "$wowi_changelog" 2>/dev/null
|
|
fi
|
|
;;
|
|
401)
|
|
echo "Error! No addon for id \"$addonid\" found or you do not have permission to upload files."
|
|
return_code=1
|
|
;;
|
|
403)
|
|
echo "Error! Incorrect api key or you do not have permission to upload files."
|
|
return_code=1
|
|
;;
|
|
*)
|
|
echo "Error! ($result)"
|
|
if [ -s "$resultfile" ]; then
|
|
echo "$(<"$resultfile")"
|
|
fi
|
|
return_code=1
|
|
;;
|
|
esac
|
|
else
|
|
return_code=1
|
|
fi
|
|
echo
|
|
|
|
rm -f "$resultfile" 2>/dev/null
|
|
|
|
return "$return_code"
|
|
}
|
|
|
|
# Upload to Wago
|
|
upload_wago() {
|
|
if [[ -z "$wagoid" || -z "$wago_token" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local _wago_versions _wago_game_version _wago_support_property
|
|
_wago_versions=$( curl -s https://addons.wago.io/api/data/game | jq -c '.patches' 2>/dev/null )
|
|
if [ -n "$_wago_versions" ]; then
|
|
_wago_support_property=
|
|
local wago_type wago_game_type_version invalid_version
|
|
for type in "${!game_type_version[@]}"; do
|
|
case $type in
|
|
bcc) wago_type="bc" ;;
|
|
wrath) wago_type="wotlk" ;;
|
|
mists) wago_type="mop" ;;
|
|
*) wago_type="$type"
|
|
esac
|
|
wago_game_type_version=
|
|
IFS=':' read -ra V <<< "${game_type_version[$type]}"
|
|
for version in "${V[@]}"; do
|
|
# check the version
|
|
if ! jq -e --arg t "$wago_type" --arg v "$version" '.[$t] // empty | index($v)' <<< "$_wago_versions" &>/dev/null; then
|
|
invalid_version="$version"
|
|
# no match, so grab the next highest version (try to avoid testing versions)
|
|
version=$( echo "$_wago_versions" | jq -r --arg t "$wago_type" --arg v "$invalid_version" '.[$t] // empty | map(select(. < $v)) | max // empty' )
|
|
if [[ -z $version ]]; then
|
|
# just grab the highest version
|
|
version=$( echo "$_wago_versions" | jq -r --arg t "$wago_type" '.[$t] // empty | max // empty' )
|
|
fi
|
|
if [[ -z $version ]]; then
|
|
echo "WARNING: No Wago game version match for \"$invalid_version\", ignoring" >&2
|
|
else
|
|
echo "WARNING: No Wago game version match for \"$invalid_version\", using \"$version\"" >&2
|
|
fi
|
|
fi
|
|
if [[ -n $version && "$_wago_game_version," != *",$version," ]]; then
|
|
wago_game_type_version+=",\"${version}\""
|
|
_wago_game_version+=",${version}"
|
|
fi
|
|
done
|
|
if [[ -n $wago_game_type_version ]]; then
|
|
_wago_support_property+="\"supported_${wago_type}_patches\": [${wago_game_type_version#,}], "
|
|
fi
|
|
done
|
|
_wago_game_version=$( echo "${_wago_game_version#,}" | tr ',' '\n' | sort -Vr | tr '\n' ',' )
|
|
_wago_game_version=${_wago_game_version%,}
|
|
fi
|
|
if [ -z "$_wago_support_property" ]; then
|
|
echo "Error fetching game version info from https://addons.wago.io/api/data/game"
|
|
echo
|
|
echo "Skipping upload to Wago."
|
|
echo
|
|
return 1
|
|
fi
|
|
|
|
local _wago_payload _wago_stability
|
|
local resultfile result
|
|
local return_code=0
|
|
|
|
_wago_stability="$file_type"
|
|
if [[ "$file_type" == "release" ]]; then
|
|
_wago_stability="stable"
|
|
fi
|
|
|
|
_wago_payload=$( cat <<-EOF
|
|
{
|
|
"label": "$archive_label",
|
|
$_wago_support_property
|
|
"stability": "$_wago_stability",
|
|
"changelog": $( jq --slurp --raw-input '.' < "$changelog_path" )
|
|
}
|
|
EOF
|
|
)
|
|
|
|
echo "Uploading $archive_name ($_wago_game_version $file_type) to https://addons.wago.io/addons/$wagoid"
|
|
resultfile="$releasedir/wago_result.json"
|
|
if result=$( echo "$_wago_payload" | curl -sS --retry 3 --retry-delay 10 \
|
|
-w "%{http_code}" -o "$resultfile" \
|
|
-H "authorization: Bearer $wago_token" \
|
|
-H "accept: application/json" \
|
|
-F "metadata=<-" \
|
|
-F "file=@$archive" \
|
|
"https://addons.wago.io/api/projects/$wagoid/version"
|
|
); then
|
|
case $result in
|
|
200|201) echo "Success!" ;;
|
|
302)
|
|
echo "Error! ($result)"
|
|
# don't need to ouput the redirect page
|
|
return_code=1
|
|
;;
|
|
403)
|
|
echo "Error! ($result)"
|
|
return_code=1
|
|
;;
|
|
404)
|
|
echo "Error! No Wago project for id \"$wagoid\" found."
|
|
return_code=1
|
|
;;
|
|
*)
|
|
echo "Error! ($result)"
|
|
if [ -s "$resultfile" ]; then
|
|
echo "$(<"$resultfile")"
|
|
fi
|
|
return_code=1
|
|
;;
|
|
esac
|
|
else
|
|
return_code=1
|
|
fi
|
|
echo
|
|
|
|
rm -f "$resultfile" 2>/dev/null
|
|
|
|
return "$return_code"
|
|
}
|
|
|
|
# Create a GitHub Release for tags and upload the zipfile as an asset.
|
|
upload_github_asset() {
|
|
local asset_id result return_code=0
|
|
local _ghf_release_id=$1
|
|
local _ghf_file_name=$2
|
|
local _ghf_file_path=$3
|
|
local _ghf_resultfile="$releasedir/gh_asset_result.json"
|
|
local _ghf_content_type="application/${_ghf_file_name##*.}" # zip or json
|
|
|
|
# check if an asset exists and delete it (editing a release)
|
|
asset_id=$( curl -sS \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
"https://api.github.com/repos/$project_github_slug/releases/$_ghf_release_id/assets" \
|
|
| jq --arg file "$_ghf_file_name" '.[] | select(.name? == $file) | .id'
|
|
)
|
|
if [ -n "$asset_id" ]; then
|
|
curl -s \
|
|
-X DELETE \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
"https://api.github.com/repos/$project_github_slug/releases/assets/$asset_id" &>/dev/null
|
|
fi
|
|
|
|
echo -n "Uploading $_ghf_file_name... "
|
|
if result=$( curl -sS --retry 3 --retry-delay 10 \
|
|
-w "%{http_code}" -o "$_ghf_resultfile" \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
-H "Content-Type: $_ghf_content_type" \
|
|
--data-binary "@$_ghf_file_path" \
|
|
"https://uploads.github.com/repos/$project_github_slug/releases/$_ghf_release_id/assets?name=$_ghf_file_name"
|
|
); then
|
|
if [ "$result" = "201" ]; then
|
|
echo "Success!"
|
|
else
|
|
echo "Error ($result)"
|
|
if [ -s "$_ghf_resultfile" ]; then
|
|
echo "$(<"$_ghf_resultfile")"
|
|
fi
|
|
return_code=1
|
|
fi
|
|
else
|
|
return_code=1
|
|
fi
|
|
|
|
rm -f "$_ghf_resultfile" 2>/dev/null
|
|
|
|
return "$return_code"
|
|
}
|
|
|
|
upload_github() {
|
|
if [[ -z "$tag" || -z "$project_github_slug" || -z "$github_token" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local _gh_metadata _gh_previous_metadata _gh_payload _gh_release_url _gh_method
|
|
local release_id versionfile resultfile result flavor title
|
|
local return_code=0
|
|
|
|
title=$( echo "$project" | jq --raw-input '.' )
|
|
_gh_metadata='{ "name": '"$title"', "version": "'"$project_version"'", "filename": "'"$archive_name"'", "nolib": false, "metadata": ['
|
|
for type in "${!game_type_interface[@]}"; do
|
|
flavor="${game_flavor[$type]}"
|
|
[[ $flavor == "retail" ]] && flavor="mainline"
|
|
IFS=':' read -ra V <<< "${game_type_interface[$type]}"
|
|
for interface in "${V[@]}"; do
|
|
_gh_metadata+='{ "flavor": "'"${flavor}"'", "interface": '"${interface}"' },'
|
|
done
|
|
done
|
|
_gh_metadata=${_gh_metadata%,}
|
|
_gh_metadata+='] }'
|
|
if [ -f "$nolib_archive" ]; then
|
|
_gh_metadata+=',{ "name": '"$title"', "version": "'"$project_version"'", "filename": "'"$nolib_archive_name"'", "nolib": true, "metadata": ['
|
|
for type in "${!game_type_interface[@]}"; do
|
|
flavor="${game_flavor[$type]}"
|
|
[[ $flavor == "retail" ]] && flavor="mainline"
|
|
IFS=':' read -ra V <<< "${game_type_interface[$type]}"
|
|
for interface in "${V[@]}"; do
|
|
_gh_metadata+='{ "flavor": "'"${flavor}"'", "interface": '"${interface}"' },'
|
|
done
|
|
done
|
|
_gh_metadata=${_gh_metadata%,}
|
|
_gh_metadata+='] }'
|
|
fi
|
|
_gh_metadata='{ "releases": ['"$_gh_metadata"'] }'
|
|
|
|
versionfile="$releasedir/release.json"
|
|
jq -c '.' <<< "$_gh_metadata" > "$versionfile" || echo "There was an error creating release.json" >&2
|
|
|
|
_gh_payload=$( cat <<-EOF
|
|
{
|
|
"tag_name": "$tag",
|
|
"name": "$tag",
|
|
"body": $( jq --slurp --raw-input '.' < "$changelog_path" ),
|
|
"draft": false,
|
|
"prerelease": $( [[ "$file_type" != "release" ]] && echo true || echo false )
|
|
}
|
|
EOF
|
|
)
|
|
resultfile="$releasedir/gh_result.json"
|
|
|
|
release_id=$( curl -sS \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
"https://api.github.com/repos/$project_github_slug/releases/tags/$tag" \
|
|
| jq '.id // empty'
|
|
)
|
|
if [ -n "$release_id" ]; then
|
|
echo "Updating GitHub release: https://github.com/$project_github_slug/releases/tag/$tag"
|
|
_gh_release_url="https://api.github.com/repos/$project_github_slug/releases/$release_id"
|
|
_gh_method="PATCH"
|
|
|
|
# combine version info
|
|
_gh_metadata_url=$( curl -sS \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
"https://api.github.com/repos/$project_github_slug/releases/$release_id/assets" \
|
|
| jq -r '.[] | select(.name? == "release.json") | .url // empty'
|
|
)
|
|
if [ -n "$_gh_metadata_url" ]; then
|
|
if _gh_previous_metadata=$( curl -sSL --fail \
|
|
-H "Accept: application/octet-stream" \
|
|
-H "Authorization: token $github_token" \
|
|
"$_gh_metadata_url"
|
|
); then
|
|
jq -sc '.[0].releases + .[1].releases | unique_by(.filename) | { releases: [.[]] }' <<< "${_gh_metadata} ${_gh_previous_metadata}" > "$versionfile"
|
|
else
|
|
echo "Warning: Unable to update release.json ($?)"
|
|
fi
|
|
fi
|
|
else
|
|
echo "Creating GitHub release: https://github.com/$project_github_slug/releases/tag/$tag"
|
|
_gh_release_url="https://api.github.com/repos/$project_github_slug/releases"
|
|
_gh_method="POST"
|
|
fi
|
|
if result=$( echo "$_gh_payload" | curl -sS --retry 3 --retry-delay 10 \
|
|
-w "%{http_code}" -o "$resultfile" \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
-H "Authorization: token $github_token" \
|
|
-X "$_gh_method" \
|
|
-d @- \
|
|
"$_gh_release_url"
|
|
); then
|
|
if [ "$result" = "200" ] || [ "$result" = "201" ]; then # edited || created
|
|
if [ -z "$release_id" ]; then
|
|
release_id=$( jq '.id' < "$resultfile" )
|
|
fi
|
|
upload_github_asset "$release_id" "$archive_name" "$archive"
|
|
if [ -f "$nolib_archive" ]; then
|
|
upload_github_asset "$release_id" "$nolib_archive_name" "$nolib_archive"
|
|
fi
|
|
if [ -s "$versionfile" ]; then
|
|
upload_github_asset "$release_id" "release.json" "$versionfile"
|
|
fi
|
|
else
|
|
echo "Error! ($result)"
|
|
if [ -s "$resultfile" ]; then
|
|
echo "$(<"$resultfile")"
|
|
fi
|
|
return_code=1
|
|
fi
|
|
else
|
|
return_code=1
|
|
fi
|
|
echo
|
|
|
|
rm -f "$resultfile" 2>/dev/null
|
|
[ -z "$CI" ] && rm -f "$versionfile" 2>/dev/null
|
|
|
|
return "$return_code"
|
|
}
|
|
|
|
|
|
if [[ -z $skip_upload && -n $archive && -s $archive ]]; then
|
|
if ! command -v jq &>/dev/null; then
|
|
echo "Skipping upload because \"jq\" was not found."
|
|
echo
|
|
exit_code=1
|
|
else
|
|
if ! retry upload_curseforge; then
|
|
echo "Uploading to CurseForge failed, aborting uploads." >&2
|
|
exit 1
|
|
fi
|
|
upload_wowinterface || exit_code=1
|
|
upload_wago || exit_code=1
|
|
upload_github || exit_code=1
|
|
fi
|
|
fi
|
|
|
|
# All done.
|
|
|
|
echo
|
|
echo "Packaging complete."
|
|
echo
|
|
|
|
exit "$exit_code"
|