mirror of
https://github.com/eth-p/bat-extras.git
synced 2024-12-04 21:40:44 +01:00
dev: Allow release script to be used as lib
This commit is contained in:
parent
48fe8875f7
commit
d0738b7289
378
release.sh
378
release.sh
@ -1,10 +1,256 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# bat-extras | Copyright (C) 2019-2020 eth-p | MIT License
|
# bat-extras | Copyright (C) 2019-2023 eth-p | MIT License
|
||||||
#
|
#
|
||||||
# Repository: https://github.com/eth-p/bat-extras
|
# Repository: https://github.com/eth-p/bat-extras
|
||||||
# Issues: https://github.com/eth-p/bat-extras/issues
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/build.sh"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Release-as-a-Library Functions:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Prints the path to the git workspace.
|
||||||
|
if ! batextras:is_function_defined ::get_git_workspace; then
|
||||||
|
batextras:get_git_workspace() {
|
||||||
|
batextras:lazy_done "${FUNCNAME[0]}" \
|
||||||
|
"$(batextras:get_project_directory)"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prints the commit of the latest-tagged version of bat-extras.
|
||||||
|
if ! batextras:is_function_defined ::get_current_commit; then
|
||||||
|
batextras:get_current_commit() {
|
||||||
|
batextras:lazy_done "${FUNCNAME[0]}" \
|
||||||
|
"$(batextras:git rev-parse HEAD)"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prints the commit of the latest-tagged version of bat-extras.
|
||||||
|
if ! batextras:is_function_defined ::get_previous_tag_commit; then
|
||||||
|
batextras:get_previous_tag_commit() {
|
||||||
|
local latest_tag
|
||||||
|
local before_latest_tag
|
||||||
|
{
|
||||||
|
read -r latest_tag
|
||||||
|
read -r before_latest_tag
|
||||||
|
} < <(batextras:git rev-list --tags --max-count=2)
|
||||||
|
|
||||||
|
# If the latest commit is a tag, go to the one before that.
|
||||||
|
if [[ "$(batextras:get_current_commit)" = "$latest_tag" ]]; then
|
||||||
|
latest_tag="${before_latest_tag}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
batextras:lazy_done "${FUNCNAME[0]}" "$latest_tag"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prints the ref name of the latest-tagged version of bat-extras.
|
||||||
|
if ! batextras:is_function_defined ::get_previous_tag_name; then
|
||||||
|
batextras:get_previous_tag_name() {
|
||||||
|
batextras:lazy_done "${FUNCNAME[0]}" \
|
||||||
|
"$(batextras:git describe --tags --abbrev=0 "$(batextras:get_previous_tag_commit)")"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Returns the suffix for a day of the month.
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The day number.
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# The suffix.
|
||||||
|
batextras:day_suffix() {
|
||||||
|
case "$1" in
|
||||||
|
11 | 12 | 13) echo "th" ;;
|
||||||
|
*1) echo "st" ;;
|
||||||
|
*2) echo "nd" ;;
|
||||||
|
*3) echo "rd" ;;
|
||||||
|
*) echo "th" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Runs `git` within the project directory.
|
||||||
|
#
|
||||||
|
# This takes the same arguments as git (with the exception of `-C`), and
|
||||||
|
# does exactly what `git` would normally do.
|
||||||
|
batextras:git() {
|
||||||
|
git -C "$(batextras:get_git_workspace)" "$@"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# Creates the zipball for release.
|
||||||
|
# YOU MUST BUILD THE PROJECT FIRST!
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The absolute path to the output zip file.
|
||||||
|
#
|
||||||
|
# Stderr:
|
||||||
|
# Messages.
|
||||||
|
batextras:create_package() {
|
||||||
|
local artifact="$1"
|
||||||
|
local bin_dir man_dir doc_dir
|
||||||
|
bin_dir="$(batextras:get_output_bin_directory)"
|
||||||
|
man_dir="$(batextras:get_output_man_directory)"
|
||||||
|
doc_dir="$(batextras:get_docs_directory)"
|
||||||
|
|
||||||
|
(
|
||||||
|
# Remove the old zipball, if one exists.
|
||||||
|
if [[ -f "$artifact" ]]; then
|
||||||
|
rm "$artifact" || return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add the bin directory.
|
||||||
|
cd "$(dirname -- "$bin_dir")" || return $?
|
||||||
|
zip -r "$artifact" "$(basename -- "$bin_dir")"
|
||||||
|
|
||||||
|
# Add the doc directory.
|
||||||
|
cd "$(dirname -- "$doc_dir")" || return $?
|
||||||
|
zip -ru "$artifact" "$(basename -- "$doc_dir")"
|
||||||
|
|
||||||
|
# Add the man directory.
|
||||||
|
if [[ -d "$man_dir" ]]; then
|
||||||
|
cd "$(dirname -- "$man_dir")" || return $?
|
||||||
|
zip -ru "$artifact" "$(basename -- "$man_dir")"
|
||||||
|
fi
|
||||||
|
) 1>&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generates a Markdown changelog for all changes between two commits.
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The first commit, exclusive.
|
||||||
|
# 2 -- The second commit, inclusive.
|
||||||
|
# 3 -- A filter in regex.
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# The changelog.
|
||||||
|
batextras:generate_changelog() {
|
||||||
|
local start_commit="$1"
|
||||||
|
local end_commit="$2"
|
||||||
|
local filter="${3}"
|
||||||
|
|
||||||
|
# Generate sed replacement patterns.
|
||||||
|
local script_links=()
|
||||||
|
local script_names=()
|
||||||
|
local script script_name
|
||||||
|
while read -r script; do
|
||||||
|
script_name="$(basename "$script" .sh)"
|
||||||
|
script_names+=("$script_name")
|
||||||
|
done < <(batextras:get_source_paths)
|
||||||
|
|
||||||
|
local script_pattern
|
||||||
|
script_pattern="$(printf 's/\\(%s\\)/`\\1`/;' "${script_names[@]}")"
|
||||||
|
|
||||||
|
# Generate the changelog.
|
||||||
|
local changelog=''
|
||||||
|
local commit
|
||||||
|
local affected_module
|
||||||
|
local commit_message
|
||||||
|
while read -r commit; do
|
||||||
|
commit_message="$(batextras:git show -s --format=%s "$commit")"
|
||||||
|
|
||||||
|
if ! [[ "$commit_message" =~ ^([a-z-]+):[[:space:]]*(.*)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
affected_module="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Make module names consistent.
|
||||||
|
case "$affected_module" in
|
||||||
|
dev | lib | mdroff) affected_module="developer" ;;
|
||||||
|
tests) affected_module="test" ;;
|
||||||
|
doc) affected_module="docs" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Append to changelog.
|
||||||
|
if [[ "$affected_module" =~ ^($filter)$ ]]; then
|
||||||
|
changelog="$changelog"$'\n'" - ${commit_message}"
|
||||||
|
fi
|
||||||
|
done < <(batextras:git rev-list "${start_commit}..${end_commit}")
|
||||||
|
|
||||||
|
# Print the changelog.
|
||||||
|
changelog="$(sed "$script_pattern" <<< "$changelog")"
|
||||||
|
printf "%s\n" "${changelog:1}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generates the Markdown release notes.
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The oldest commit, exclusive.
|
||||||
|
# 2 -- The newest commit, inclusive.
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# The changelog.
|
||||||
|
batextras:generate_release_notes() {
|
||||||
|
local commit_oldest="$1"
|
||||||
|
local commit_newest="$2"
|
||||||
|
local commit_newest_url="https://github.com/eth-p/bat-extras/tree/${commit_newest}"
|
||||||
|
local date_str
|
||||||
|
|
||||||
|
# Get the commit date.
|
||||||
|
local date_year date_month date_day date_month_text date_day_suffix
|
||||||
|
read -r date_year date_month date_day date_month_text \
|
||||||
|
< <(batextras:git show -s --format="%cd" --date="format:%Y %m %d %B" "$commit_newest")
|
||||||
|
|
||||||
|
date_day_suffix="$(batextras:day_suffix "$date_day")"
|
||||||
|
date_str="${date_month_text} ${date_day}${date_day_suffix}, ${date_year}"
|
||||||
|
|
||||||
|
# For each built script, we want to:
|
||||||
|
# - Get the name of the script.
|
||||||
|
# - Get a link to the documentation.
|
||||||
|
# - Add it to the filter for non-developer items.
|
||||||
|
local script_name script_names script_links script_filters script_list_markdown
|
||||||
|
script_links=()
|
||||||
|
script_names=()
|
||||||
|
script_filters=''
|
||||||
|
|
||||||
|
while read -r script; do
|
||||||
|
script_name="$(basename "$script" .sh)"
|
||||||
|
script_names+=("$script_name")
|
||||||
|
script_links+=("[\`${script_name}\`](https://github.com/eth-p/bat-extras/blob/${commit_newest}/doc/${script_name}.md)")
|
||||||
|
script_filters="${script_filters}|$(printf "%q" "$script_name")"
|
||||||
|
done < <(batextras:get_source_paths)
|
||||||
|
|
||||||
|
script_filters="${script_filters:1}" # Remove the leading "|"
|
||||||
|
script_list_markdown="$(printf "%s, " "${script_links[@]:0:$((${#script_links[@]} - 1))}")"
|
||||||
|
script_list_markdown="${script_list_markdown}and ${script_links[$((${#script_links[@]} - 1))]}"
|
||||||
|
|
||||||
|
# Get the changelog.
|
||||||
|
local changelog changelog_dev
|
||||||
|
changelog="$(batextras:generate_changelog "$commit_oldest" "$commit_newest" "$script_filters")"
|
||||||
|
changelog_dev="$(batextras:generate_changelog "$commit_oldest" "$commit_newest" "test|developer|ci|build")"
|
||||||
|
|
||||||
|
# Print the template.
|
||||||
|
{ sed '/\\$/{N;s/\\\n//;s/\n//p;}'; } <<- EOF
|
||||||
|
This contains the latest versions of ${script_list_markdown} as of commit [${commit_newest}](${commit_newest_url}) (${date_str}).
|
||||||
|
|
||||||
|
**This is provided as a convenience only.**
|
||||||
|
I would still recommend following the installation instructions in [the README](https://github.com/eth-p/bat-extras#installation-) for the most up-to-date versions.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
${changelog}
|
||||||
|
|
||||||
|
### Developer
|
||||||
|
<details>
|
||||||
|
<div markdown="1">
|
||||||
|
|
||||||
|
${changelog_dev}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Main:
|
||||||
|
# Only run everything past this point if the script is not sourced.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
(return 0 2>/dev/null) && return 0
|
||||||
|
|
||||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
DATE="$(date +%Y%m%d)"
|
DATE="$(date +%Y%m%d)"
|
||||||
VERSION="$(< "${HERE}/version.txt")"
|
VERSION="$(< "${HERE}/version.txt")"
|
||||||
@ -14,22 +260,23 @@ SRC="$HERE/src"
|
|||||||
source "${LIB}/print.sh"
|
source "${LIB}/print.sh"
|
||||||
source "${LIB}/opt.sh"
|
source "${LIB}/opt.sh"
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Options.
|
# Options.
|
||||||
OPT_ARTIFACT="bat-extras-${DATE}.zip"
|
OPT_ARTIFACT="bat-extras-${DATE}.zip"
|
||||||
OPT_SINCE=
|
OPT_SINCE=
|
||||||
OPT_BAD_IDEA=false
|
OPT_BAD_IDEA=false
|
||||||
OPT_BIN_DIR="$HERE/bin"
|
OPT_BIN_DIR="$(batextras:get_output_bin_directory)"
|
||||||
OPT_DOC_DIR="$HERE/doc"
|
OPT_DOC_DIR="$(batextras:get_docs_directory)"
|
||||||
OPT_MAN_DIR="$HERE/man"
|
OPT_MAN_DIR="$(batextras:get_output_man_directory)"
|
||||||
|
|
||||||
while shiftopt; do
|
while shiftopt; do
|
||||||
case "$OPT" in
|
case "$OPT" in
|
||||||
--since)
|
--since)
|
||||||
shiftval
|
shiftval
|
||||||
OPT_SINCE="$OPT_VAL"
|
OPT_SINCE="$OPT_VAL"
|
||||||
if ! git rev-parse "$OPT_SINCE" &> /dev/null; then
|
if ! batextras:git rev-parse "$OPT_SINCE" &> /dev/null; then
|
||||||
printc "%{RED}%s: unknown commit or tag for '%s'\n" "$PROGRAM" "$OPT"
|
printc "%{RED}%s: unknown commit or tag for '%s'\n" "$PROGRAM" "$OPT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -48,6 +295,10 @@ done
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Verify the version matches today's date.
|
# Verify the version matches today's date.
|
||||||
|
|
||||||
|
VERSION="$(source "${LIB}/constants.sh" && echo "${PROGRAM_VERSION}")"
|
||||||
|
VERSION_EXPECTED="$(date +%Y.%m.%d)"
|
||||||
|
|
||||||
if [[ "$VERSION" != "$VERSION_EXPECTED" ]] && ! "$OPT_BAD_IDEA"; then
|
if [[ "$VERSION" != "$VERSION_EXPECTED" ]] && ! "$OPT_BAD_IDEA"; then
|
||||||
printc "%{RED}The expected version does not match %{DEFAULT}version.txt%{RED}!%{CLEAR}\n"
|
printc "%{RED}The expected version does not match %{DEFAULT}version.txt%{RED}!%{CLEAR}\n"
|
||||||
printc "%{RED}Expected: %{YELLOW}%s%{CLEAR}\n" "$VERSION_EXPECTED"
|
printc "%{RED}Expected: %{YELLOW}%s%{CLEAR}\n" "$VERSION_EXPECTED"
|
||||||
@ -55,6 +306,18 @@ if [[ "$VERSION" != "$VERSION_EXPECTED" ]] && ! "$OPT_BAD_IDEA"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Verify the working tree is clean-ish.
|
||||||
|
|
||||||
|
if ! "$OPT_BAD_IDEA"; then
|
||||||
|
while read -r flags file; do
|
||||||
|
if [[ "$flags" =~ M ]]; then
|
||||||
|
printc "%{RED}Found an uncommitted change in %{DEFAULT}%s%{RED}!%{CLEAR}\n" "$file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done < <(batextras:git status --porcelain)
|
||||||
|
fi
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Build files.
|
# Build files.
|
||||||
|
|
||||||
@ -75,110 +338,13 @@ printc "%{YELLOW}Building scripts...%{CLEAR}\n"
|
|||||||
# Build package.
|
# Build package.
|
||||||
|
|
||||||
printc "%{YELLOW}Packaging artifacts...%{CLEAR}\n"
|
printc "%{YELLOW}Packaging artifacts...%{CLEAR}\n"
|
||||||
(
|
batextras:create_package "$OPT_ARTIFACT"
|
||||||
rm "$OPT_ARTIFACT"
|
|
||||||
cd "$(dirname "$OPT_BIN_DIR")"
|
|
||||||
zip -r "$OPT_ARTIFACT" "$(basename "$OPT_BIN_DIR")"
|
|
||||||
cd "$(dirname "$OPT_DOC_DIR")"
|
|
||||||
zip -ru "$OPT_ARTIFACT" "$(basename "$OPT_DOC_DIR")"
|
|
||||||
if [[ -d "$OPT_MAN_DIR" ]]; then
|
|
||||||
cd "$(dirname "$OPT_MAN_DIR")"
|
|
||||||
zip -ru "$OPT_ARTIFACT" "$(basename "$OPT_MAN_DIR")"
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
|
|
||||||
printc "%{YELLOW}Package created as %{BLUE}%s%{YELLOW}.%{CLEAR}\n" "$OPT_ARTIFACT"
|
printc "%{YELLOW}Package created as %{BLUE}%s%{YELLOW}.%{CLEAR}\n" "$OPT_ARTIFACT"
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Print template description package.
|
# Print template description package.
|
||||||
|
|
||||||
printc "%{YELLOW}Release description:%{CLEAR}\n"
|
printc "%{YELLOW}Release description:%{CLEAR}\n"
|
||||||
|
batextras:generate_release_notes \
|
||||||
# Get the commit hash.
|
"$(batextras:get_previous_tag_name)" \
|
||||||
COMMIT="$(git rev-parse HEAD)"
|
"$(batextras:get_current_commit)"
|
||||||
COMMIT_URL="https://github.com/eth-p/bat-extras/tree/${COMMIT}"
|
|
||||||
|
|
||||||
# Get the release date string.
|
|
||||||
DATE_DAY="$(date +%e | sed 's/ //')"
|
|
||||||
DATE_SUFFIX=""
|
|
||||||
case "$DATE_DAY" in
|
|
||||||
11 | 12 | 13) DATE_SUFFIX="th" ;;
|
|
||||||
*1) DATE_SUFFIX="st" ;;
|
|
||||||
*2) DATE_SUFFIX="nd" ;;
|
|
||||||
*3) DATE_SUFFIX="rd" ;;
|
|
||||||
*) DATE_SUFFIX="th" ;;
|
|
||||||
esac
|
|
||||||
DATE_STR="$(date +'%B') ${DATE_DAY}${DATE_SUFFIX}, $(date +'%Y')"
|
|
||||||
|
|
||||||
# Get the script names.
|
|
||||||
script_links=()
|
|
||||||
script_names=()
|
|
||||||
for script in "$SRC"/*.sh; do
|
|
||||||
script_name="$(basename "$script" .sh)"
|
|
||||||
script_names+=("$script_name")
|
|
||||||
script_links+=("[\`${script_name}\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/${script_name}.md)")
|
|
||||||
done
|
|
||||||
|
|
||||||
script_pattern="$(printf 's/\\(%s\\)/`\\1`/;' "${script_names[@]}")"
|
|
||||||
SCRIPTS="$(printf "%s, " "${script_links[@]:0:$((${#script_links[@]} - 1))}")"
|
|
||||||
SCRIPTS="${SCRIPTS}and ${script_links[$((${#script_links[@]} - 1))]}"
|
|
||||||
|
|
||||||
# Get the changelog.
|
|
||||||
CHANGELOG_DEV=''
|
|
||||||
CHANGELOG=''
|
|
||||||
if [[ -n "$OPT_SINCE" ]]; then
|
|
||||||
ref="$(git rev-parse HEAD)"
|
|
||||||
end="$(git rev-parse "$OPT_SINCE")"
|
|
||||||
while [[ "$ref" != "$end" ]]; do
|
|
||||||
is_developer=false
|
|
||||||
ref_message="$(git show -s --format=%s "$ref")"
|
|
||||||
ref="$(git rev-parse "${ref}~1")"
|
|
||||||
|
|
||||||
if [[ "$ref_message" =~ ^([a-z-]+):[[:space:]]*(.*)$ ]]; then
|
|
||||||
affected_module="${BASH_REMATCH[1]}"
|
|
||||||
|
|
||||||
# Make module names consistent.
|
|
||||||
case "$affected_module" in
|
|
||||||
dev | lib | mdroff) affected_module="developer" ;;
|
|
||||||
tests) affected_module="test" ;;
|
|
||||||
doc) affected_module="docs" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Switch to the correct changelog.
|
|
||||||
case "$affected_module" in
|
|
||||||
test | developer | ci | build) is_developer=true ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Append to changelog.
|
|
||||||
if "$is_developer"; then
|
|
||||||
CHANGELOG_DEV="$CHANGELOG_DEV"$'\n'" - ${ref_message}"
|
|
||||||
else
|
|
||||||
CHANGELOG="$CHANGELOG"$'\n'" - ${ref_message}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
CHANGELOG="$(sed "$script_pattern" <<< "$CHANGELOG")"
|
|
||||||
CHANGELOG_DEV="$(sed "$script_pattern" <<< "$CHANGELOG_DEV")"
|
|
||||||
|
|
||||||
# Print the template.
|
|
||||||
sed '/\\$/{N;s/\\\n//;s/\n//p;}' <<- EOF
|
|
||||||
This contains the latest versions of $SCRIPTS as of commit [$(git rev-parse --short HEAD)]($COMMIT_URL) (${DATE_STR}).
|
|
||||||
|
|
||||||
**This is provided as a convenience only.**
|
|
||||||
I would still recommend following the installation instructions in \
|
|
||||||
[the README](https://github.com/eth-p/bat-extras#installation-) for the most up-to-date versions.
|
|
||||||
|
|
||||||
### Changes
|
|
||||||
$CHANGELOG
|
|
||||||
|
|
||||||
### Developer
|
|
||||||
<details>
|
|
||||||
<div markdown="1">
|
|
||||||
|
|
||||||
$CHANGELOG_DEV
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
EOF
|
|
||||||
|
Loading…
Reference in New Issue
Block a user