sourcegit/build/resources/appimage/publish-appimage

708 lines
No EOL
18 KiB
Bash
Executable file

#!/bin/bash
################################################################################
# PROJECT : Publish-AppImage for .NET
# WEBPAGE : https://github.com/kuiperzone/Publish-AppImage
# COPYRIGHT : Andy Thomas 2021-2023
# LICENSE : MIT
################################################################################
###############################
# CONSTANTS
###############################
declare -r _SCRIPT_VERSION="1.3.1"
declare -r _SCRIPT_TITLE="Publish-AppImage for .NET"
declare -r _SCRIPT_IMPL_MIN=1
declare -r _SCRIPT_IMPL_MAX=1
declare -r _SCRIPT_COPYRIGHT="Copyright 2023 Andy Thomas"
declare -r _SCRIPT_WEBSITE="https://github.com/kuiperzone/Publish-AppImage"
declare -r _SCRIPT_NAME="publish-appimage"
declare -r _DEFAULT_CONF="${_SCRIPT_NAME}.conf"
declare -r _APPIMAGE_KIND="appimage"
declare -r _ZIP_KIND="zip"
declare -r _DOTNET_NONE="null"
###############################
# FUNCTIONS
###############################
function assert_result
{
local _ret=$?
if [ ${_ret} -ne 0 ]; then
echo
exit ${_ret}
fi
}
function exec_or_die
{
echo "${1}"
eval "${1}"
assert_result
}
function ensure_directory
{
local _path="${1}"
if [ ! -d "${_path}" ]; then
mkdir -p "${_path}"
assert_result
fi
}
function remove_path
{
local _path="${1}"
if [ -d "${_path}" ]; then
rm -rf "${_path}"
assert_result
elif [ -f "${_path}" ]; then
rm -f "${_path}"
assert_result
fi
}
function assert_mandatory
{
local _name="${1}"
local _value="${2}"
if [ "${_value}" == "" ]; then
echo "${_name} undefined in: ${_conf_arg_value}"
echo
exit 1
fi
}
function assert_opt_file
{
local _name="${1}"
local _value="${2}"
if [ "${_value}" != "" ] && [ ! -f "${_value}" ]; then
echo "File not found: ${_value}"
if [ "${_name}" != "" ]; then
echo "See ${_name} in: ${_conf_arg_value}"
fi
echo
exit 1
fi
}
###############################
# HANDLE ARGUMENTS
###############################
# Specify conf file
declare -r _CONF_ARG="f"
declare -r _CONF_ARG_NAME="conf"
_conf_arg_value="${_DEFAULT_CONF}"
_arg_syntax=":${_CONF_ARG}:"
# Runtime ID
declare -r _RID_ARG="r"
declare -r _RID_ARG_NAME="runtime"
_rid_arg_value="linux-x64"
_arg_syntax="${_arg_syntax}${_RID_ARG}:"
# Package kind
declare -r _KIND_ARG="k"
declare -r _KIND_ARG_NAME="kind"
declare -l _kind_arg_value="${_APPIMAGE_KIND}"
_arg_syntax="${_arg_syntax}${_KIND_ARG}:"
# Run app
declare -r _RUNAPP_ARG="u"
declare -r _RUNAPP_ARG_NAME="run"
_runapp_arg_value=false
_arg_syntax="${_arg_syntax}${_RUNAPP_ARG}"
# Verbose
declare -r _VERBOSE_ARG="b"
declare -r _VERBOSE_ARG_NAME="verbose"
_verbose_arg_value=false
_arg_syntax="${_arg_syntax}${_VERBOSE_ARG}"
# Skip yes (no prompt)
declare -r _SKIPYES_ARG="y"
declare -r _SKIPYES_ARG_NAME="skip-yes"
_skipyes_arg_value=false
_arg_syntax="${_arg_syntax}${_SKIPYES_ARG}"
# Output name
declare -r _OUTPUT_ARG="o"
declare -r _OUTPUT_ARG_NAME="output"
_output_arg_value=""
_arg_syntax="${_arg_syntax}${_OUTPUT_ARG}:"
# Show version
declare -r _VERSION_ARG="v"
declare -r _VERSION_ARG_NAME="version"
_version_arg_value=false
_arg_syntax="${_arg_syntax}${_VERSION_ARG}"
# Show help
declare -r _HELP_ARG="h"
declare -r _HELP_ARG_NAME="help"
_help_arg_value=false
_arg_syntax="${_arg_syntax}${_HELP_ARG}"
_exit_help=0
# Transform long options to short ones
for arg in "${@}"; do
shift
case "${arg}" in
("--${_CONF_ARG_NAME}") set -- "$@" "-${_CONF_ARG}" ;;
("--${_RID_ARG_NAME}") set -- "$@" "-${_RID_ARG}" ;;
("--${_KIND_ARG_NAME}") set -- "$@" "-${_KIND_ARG}" ;;
("--${_RUNAPP_NAME}") set -- "$@" "-${_RUNAPP_ARG}" ;;
("--${_VERBOSE_ARG_NAME}") set -- "$@" "-${_VERBOSE_ARG}" ;;
("--${_SKIPYES_ARG_NAME}") set -- "$@" "-${_SKIPYES_ARG}" ;;
("--${_OUTPUT_ARG_NAME}") set -- "$@" "-${_OUTPUT_ARG}" ;;
("--${_VERSION_ARG_NAME}") set -- "$@" "-${_VERSION_ARG}" ;;
("--${_HELP_ARG_NAME}") set -- "$@" "-${_HELP_ARG}" ;;
("--"*)
echo "Illegal argument: ${arg}"
echo
_exit_help=1
break
;;
(*) set -- "$@" "${arg}" ;;
esac
done
if [ ${_exit_help} == 0 ]; then
# Read arguments
while getopts ${_arg_syntax} arg; do
case "${arg}" in
(${_CONF_ARG}) _conf_arg_value="${OPTARG}" ;;
(${_RID_ARG}) _rid_arg_value="${OPTARG}" ;;
(${_KIND_ARG}) _kind_arg_value="${OPTARG}" ;;
(${_RUNAPP_ARG}) _runapp_arg_value=true ;;
(${_VERBOSE_ARG}) _verbose_arg_value=true ;;
(${_SKIPYES_ARG}) _skipyes_arg_value=true ;;
(${_OUTPUT_ARG}) _output_arg_value="${OPTARG}" ;;
(${_VERSION_ARG}) _version_arg_value=true ;;
(${_HELP_ARG}) _help_arg_value=true ;;
(*)
echo "Illegal argument"
echo
_exit_help=1
break
;;
esac
done
fi
# Handle and help and version
if [ ${_help_arg_value} == true ] || [ $_exit_help != 0 ]; then
_indent=" "
echo "Usage:"
echo "${_indent}${_SCRIPT_NAME} [-flags] [-option-n value-n]"
echo
echo "Help Options:"
echo "${_indent}-${_HELP_ARG}, --${_HELP_ARG_NAME}"
echo "${_indent}Show help information flag."
echo
echo "${_indent}-${_VERSION_ARG}, --${_VERSION_ARG_NAME}"
echo "${_indent}Show version and about information flag."
echo
echo "Build Options:"
echo "${_indent}-${_CONF_ARG}, --${_CONF_ARG_NAME} value"
echo "${_indent}Specifies the conf file. Defaults to ${_SCRIPT_NAME}.conf."
echo
echo "${_indent}-${_RID_ARG}, --${_RID_ARG_NAME} value"
echo "${_indent}Dotnet publish runtime identifier. Valid examples include:"
echo "${_indent}linux-x64 and linux-arm64. Default is linux-x64 if unspecified."
echo "${_indent}See also: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog"
echo
echo "${_indent}-${_KIND_ARG}, --${_KIND_ARG_NAME} value"
echo "${_indent}Package output kind. Value must be one of: ${_APPIMAGE_KIND} or ${_ZIP_KIND}."
echo "${_indent}Default is ${_APPIMAGE_KIND} if unspecified."
echo
echo "${_indent}-${_VERBOSE_ARG}, --${_VERBOSE_ARG_NAME}"
echo "${_indent}Verbose review info output flag."
echo
echo "${_indent}-${_RUNAPP_ARG}, --${_RUNAPP_ARG_NAME}"
echo "${_indent}Run the application after successful build flag."
echo
echo "${_indent}-${_SKIPYES_ARG}, --${_SKIPYES_ARG_NAME}"
echo "${_indent}Skip confirmation prompt flag (assumes yes)."
echo
echo "${_indent}-${_OUTPUT_ARG}, --${_OUTPUT_ARG_NAME}"
echo "${_indent}Explicit final output filename (excluding directory part)."
echo
echo "Example:"
echo "${_indent}${_SCRIPT_NAME} -${_RID_ARG} linux-arm64"
echo
exit $_exit_help
fi
if [ ${_version_arg_value} == true ]; then
echo
echo "${_SCRIPT_TITLE}, ${_SCRIPT_VERSION}"
echo "${_SCRIPT_COPYRIGHT}"
echo "${_SCRIPT_WEBSITE}"
echo
echo "MIT License"
echo
echo "Permission is hereby granted, free of charge, to any person obtaining a copy"
echo "of this software and associated documentation files (the "Software"), to deal"
echo "in the Software without restriction, including without limitation the rights"
echo "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell"
echo "copies of the Software, and to permit persons to whom the Software is"
echo "furnished to do so, subject to the following conditions:"
echo
echo "The above copyright notice and this permission notice shall be included in all"
echo "copies or substantial portions of the Software."
echo
echo "THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR"
echo "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,"
echo "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE"
echo "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER"
echo "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,"
echo "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE"
echo "SOFTWARE."
echo
exit 0
fi
###############################
# SOURCE & WORKING
###############################
# Export these now as may be
# useful in an advanced config file
export DOTNET_RID="${_rid_arg_value}"
export PKG_KIND="${_kind_arg_value}"
export ISO_DATE=`date +"%Y-%m-%d"`
if [ ! -f "${_conf_arg_value}" ]; then
echo "Configuration file not found: ${_conf_arg_value}"
echo
exit 1
fi
# Export contents to any post publish command
set -a
# Source local to PWD
source "${_conf_arg_value}"
set +a
# For AppImage tool and backward compatibility
export VERSION="${APP_VERSION}"
# Then change PWD to conf file
cd "$(dirname "${_conf_arg_value}")"
###############################
# SANITY
###############################
if (( ${CONF_IMPL_VERSION} < ${_SCRIPT_IMPL_MIN} )) || (( ${CONF_IMPL_VERSION} > ${_SCRIPT_IMPL_MAX} )); then
echo "Configuration format version ${_SCRIPT_IMPL_VERSION} not compatible"
echo "Older conf file but newer ${_SCRIPT_NAME} implementation?"
echo "Update from: ${_SCRIPT_WEBSITE}"
echo
exit 1
fi
assert_mandatory "APP_MAIN" "${APP_MAIN}"
assert_mandatory "APP_ID" "${APP_ID}"
assert_mandatory "APP_ICON_SRC" "${APP_ICON_SRC}"
assert_mandatory "DE_NAME" "${DE_NAME}"
assert_mandatory "DE_CATEGORIES" "${DE_CATEGORIES}"
assert_mandatory "PKG_OUTPUT_DIR" "${PKG_OUTPUT_DIR}"
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
assert_mandatory "APPIMAGETOOL_COMMAND" "${APPIMAGETOOL_COMMAND}"
fi
assert_opt_file "APP_ICON_SRC" "${APP_ICON_SRC}"
assert_opt_file "APP_XML_SRC" "${APP_XML_SRC}"
if [ "${DE_TERMINAL_FLAG}" != "true" ] && [ "${DE_TERMINAL_FLAG}" != "false" ]; then
echo "DE_TERMINAL_FLAG invalid value: ${DE_TERMINAL_FLAG}"
echo
exit 1
fi
if [ "${DOTNET_PROJECT_PATH}" == "${_DOTNET_NONE}" ] && [ "${POST_PUBLISH}" == "" ]; then
echo "No publish or build operation defined (nothing will be built)"
echo "See DOTNET_PROJECT_PATH and POST_PUBLISH in: ${_conf_arg_value}"
echo
exit 1
fi
if [ "${DOTNET_PROJECT_PATH}" != "" ] && [ "${DOTNET_PROJECT_PATH}" != "${_DOTNET_NONE}" ] &&
[ ! -f "${DOTNET_PROJECT_PATH}" ] && [ ! -d "${DOTNET_PROJECT_PATH}" ]; then
echo "DOTNET_PROJECT_PATH path not found: ${DOTNET_PROJECT_PATH}"
echo
exit 1
fi
if [ "${_kind_arg_value}" != "${_APPIMAGE_KIND}" ] && [ "${_kind_arg_value}" != "${_ZIP_KIND}" ]; then
echo "Invalid argument value: ${_kind_arg_value}"
echo "Use one of: ${_APPIMAGE_KIND} or ${_ZIP_KIND}"
echo
exit 1
fi
# Detect if publish for windows
_exec_ext=""
declare -l _tw="${_rid_arg_value}"
if [[ "${_tw}" == "win"* ]]; then
# May use this in future
_exec_ext=".exe"
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
echo "Invalid AppImage payload"
echo "Looks like a windows binary to be packaged as AppImage."
echo "Use --${_KIND_ARG_NAME} ${_ZIP_KIND} instead."
echo
exit 1
fi
fi
###############################
# VARIABLES
###############################
# Abbreviate RID where it maps well to arch
if [ "${_rid_arg_value}" == "linux-x64" ]; then
_file_out_arch="-x86_64"
elif [ "${_rid_arg_value}" == "linux-arm64" ]; then
_file_out_arch="-aarch64"
else
# Otherwise use RID itself
_file_out_arch="-${_rid_arg_value}"
fi
# APPDIR LOCATIONS
export APPDIR_ROOT="${PKG_OUTPUT_DIR}/AppDir"
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
# AppImage
export APPDIR_USR="${APPDIR_ROOT}/usr"
export APPDIR_BIN="${APPDIR_ROOT}/usr/bin"
export APPDIR_SHARE="${APPDIR_ROOT}/usr/share"
_local_run="usr/bin/${APP_MAIN}${_exec_ext}"
else
# Simple zip
export APPDIR_USR=""
export APPDIR_BIN="${APPDIR_ROOT}"
export APPDIR_SHARE="${APPDIR_ROOT}"
_local_run="${APP_MAIN}${_exec_ext}"
fi
export APPRUN_TARGET="${APPDIR_BIN}/${APP_MAIN}${_exec_ext}"
# DOTNET PUBLISH
if [ "${DOTNET_PROJECT_PATH}" != "${_DOTNET_NONE}" ]; then
_publish_cmd="dotnet publish"
if [ "${DOTNET_PROJECT_PATH}" != "" ] && [ "${DOTNET_PROJECT_PATH}" != "." ]; then
_publish_cmd="${_publish_cmd} \"${DOTNET_PROJECT_PATH}\""
fi
_publish_cmd="${_publish_cmd} -r ${_rid_arg_value}"
if [ "${APP_VERSION}" != "" ]; then
_publish_cmd="${_publish_cmd} -p:Version=${APP_VERSION}"
fi
if [ "${DOTNET_PUBLISH_ARGS}" != "" ]; then
_publish_cmd="${_publish_cmd} ${DOTNET_PUBLISH_ARGS}"
fi
_publish_cmd="${_publish_cmd} -o \"${APPDIR_BIN}\""
fi
# PACKAGE OUTPUT
if [ $PKG_VERSION_FLAG == true ] && [ "${APP_VERSION}" != "" ]; then
_version_out="-${APP_VERSION}"
fi
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
# AppImageTool
if [ "${_output_arg_value}" != "" ]; then
_package_out="${PKG_OUTPUT_DIR}/${_output_arg_value}"
else
_package_out="${PKG_OUTPUT_DIR}/${APP_MAIN}${_version_out}${_file_out_arch}${PKG_APPIMAGE_SUFFIX}"
fi
_package_cmd="${APPIMAGETOOL_COMMAND}"
if [ "${PKG_APPIMAGE_ARGS}" != "" ]; then
_package_cmd="${_package_cmd} ${PKG_APPIMAGE_ARGS}"
fi
_package_cmd="${_package_cmd} \"${APPDIR_ROOT}\" \"${_package_out}\""
if [ ${_runapp_arg_value} == true ]; then
_packrun_cmd="${_package_out}"
fi
else
# Simple zip
if [ "${_output_arg_value}" != "" ]; then
_package_out="${PKG_OUTPUT_DIR}/${_output_arg_value}"
else
_package_out="${PKG_OUTPUT_DIR}/${APP_MAIN}${_version_out}${_file_out_arch}.zip"
fi
_package_cmd="(cd \"${APPDIR_ROOT}\" && zip -r \"${PWD}/${_package_out}\" ./)"
if [ ${_runapp_arg_value} == true ]; then
_packrun_cmd="${APPRUN_TARGET}"
fi
fi
###############################
# DESKTOP ENTRY & APPDATA
###############################
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
_desktop="[Desktop Entry]\n"
_desktop="${_desktop}Type=Application\n"
_desktop="${_desktop}Name=${DE_NAME}\n"
_desktop="${_desktop}Exec=AppRun\n"
_desktop="${_desktop}Terminal=${DE_TERMINAL_FLAG}\n"
_desktop="${_desktop}Categories=${DE_CATEGORIES}\n"
# Follow app-id
_desktop="${_desktop}Icon=${APP_ID}\n"
if [ "${DE_COMMENT}" != "" ]; then
_desktop="${_desktop}Comment=${DE_COMMENT}\n"
fi
if [ "${DE_KEYWORDS}" != "" ]; then
_desktop="${_desktop}Keywords=${DE_KEYWORDS}\n"
fi
_desktop="${_desktop}${DE_EXTEND}\n"
fi
# Load appdata.xml
if [ "${APP_XML_SRC}" != "" ]; then
if command -v envsubst &> /dev/null; then
_appxml=$(envsubst <"${APP_XML_SRC}")
else
_appxml=$(<"${APP_XML_SRC}")
echo "WARNING: Variable substitution not available for: ${APP_XML_SRC}"
echo
fi
fi
###############################
# DISPLAY & CONFIRM
###############################
echo "${_SCRIPT_TITLE}, ${_SCRIPT_VERSION}"
echo "${_SCRIPT_COPYRIGHT}"
echo
echo "APP_MAIN: ${APP_MAIN}"
echo "APP_ID: ${APP_ID}"
echo "APP_VERSION: ${APP_VERSION}"
echo "OUTPUT: ${_package_out}"
echo
if [ "${_desktop}" != "" ]; then
echo -e "${_desktop}"
fi
if [ ${_verbose_arg_value} == true ] && [ "${_appxml}" != "" ]; then
echo -e "${_appxml}\n"
fi
echo "Build Commands:"
if [ "${_publish_cmd}" != "" ]; then
echo
echo "${_publish_cmd}"
fi
if [ "${POST_PUBLISH}" != "" ]; then
echo
echo "${POST_PUBLISH}"
fi
echo
echo "${_package_cmd}"
echo
# Prompt
if [ $_skipyes_arg_value == false ]; then
echo
read -p "Build now [N/y]? " prompt
if [ "${prompt}" != "y" ] && [ "${prompt}" != "Y" ]; then
echo
exit 1
fi
# Continue
echo
fi
###############################
# PUBLISH & BUILD
###############################
# Clean and ensure directoy exists
ensure_directory "${PKG_OUTPUT_DIR}"
remove_path "${APPDIR_ROOT}"
remove_path "${_package_out}"
# Create AppDir structure
ensure_directory "${APPDIR_BIN}"
if [ "${_kind_arg_value}" != "${_ZIP_KIND}" ]; then
# We also create usr/share/icons, as some packages require this.
# See: https://github.com/kuiperzone/Publish-AppImage/issues/7
ensure_directory "${APPDIR_SHARE}/icons"
fi
echo
# Publish dotnet
if [ "${_publish_cmd}" != "" ]; then
exec_or_die "${_publish_cmd}"
echo
fi
# Post-publish
if [ "${POST_PUBLISH}" != "" ]; then
exec_or_die "${POST_PUBLISH}"
echo
fi
# Application file must exist!
if [ ! -f "${APPRUN_TARGET}" ]; then
echo "Expected application file not found: ${APPRUN_TARGET}"
echo
exit 1
fi
if [ "${_kind_arg_value}" == "${_APPIMAGE_KIND}" ]; then
echo
# Create desktop
if [ "${_desktop}" != "" ]; then
_file="${APPDIR_ROOT}/${APP_ID}.desktop"
echo "Creating: ${_file}"
echo -e "${_desktop}" > "${_file}"
assert_result
fi
if [ "${_appxml}" != "" ]; then
_dir="${APPDIR_SHARE}/metainfo"
_file="${_dir}/${APP_ID}.appdata.xml"
echo "Creating: ${_file}"
ensure_directory "${_dir}"
echo -e "${_appxml}" > "${_file}"
assert_result
if [ "${_desktop}" != "" ]; then
# Copy of desktop under "applications"
# Needed for launchable in appinfo.xml (if used)
# See https://github.com/AppImage/AppImageKit/issues/603
_dir="${APPDIR_SHARE}/applications"
_file="${_dir}/${APP_ID}.desktop"
echo "Creating: ${_file}"
ensure_directory "${_dir}"
echo -e "${_desktop}" > "${_file}"
assert_result
fi
fi
# Copy icon
if [ "${APP_ICON_SRC}" != "" ]; then
_icon_ext="${APP_ICON_SRC##*.}"
if [ "${_icon_ext}" != "" ]; then
_icon_ext=".${_icon_ext}"
fi
_temp="${APPDIR_ROOT}/${APP_ID}${_icon_ext}"
echo "Creating: ${_temp}"
cp "${APP_ICON_SRC}" "${_temp}"
assert_result
fi
# AppRun
_temp="${APPDIR_ROOT}/AppRun"
if [ ! -f "${_temp}" ]; then
echo "Creating: ${_temp}"
ln -s "${_local_run}" "${_temp}"
assert_result
fi
fi
# Build package
echo
exec_or_die "${_package_cmd}"
echo
echo "OUTPUT OK: ${_package_out}"
echo
if [ "${_packrun_cmd}" != "" ]; then
echo "RUNNING ..."
exec_or_die "${_packrun_cmd}"
echo
fi
exit 0