mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-11-24 08:42:19 +02:00
Build Sonarr on Net6
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
This commit is contained in:
parent
f79ae77a3a
commit
878d1561aa
256
.editorconfig
256
.editorconfig
@ -2,13 +2,267 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*.{cs}]
|
||||
# NOTE: Requires **VS2019 16.3** or later
|
||||
|
||||
# Stylecop.ruleset
|
||||
# Description: Rules for Sonarr
|
||||
|
||||
# Code files
|
||||
[*.cs]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
# Avoid "this." and "Me." if not necessary
|
||||
dotnet_style_qualification_for_field = false:refactoring
|
||||
dotnet_style_qualification_for_property = false:refactoring
|
||||
dotnet_style_qualification_for_method = false:refactoring
|
||||
dotnet_style_qualification_for_event = false:refactoring
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_naming_style.instance_field_style.capitalization = camel_case
|
||||
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||
|
||||
# Prefer "var" everywhere
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
|
||||
# Stylecop Rules
|
||||
dotnet_diagnostic.SA0001.severity = none
|
||||
dotnet_diagnostic.SA1005.severity = none
|
||||
dotnet_diagnostic.SA1025.severity = none
|
||||
dotnet_diagnostic.SA1101.severity = none
|
||||
dotnet_diagnostic.SA1116.severity = none
|
||||
dotnet_diagnostic.SA1118.severity = none
|
||||
dotnet_diagnostic.SA1122.severity = none
|
||||
dotnet_diagnostic.SA1201.severity = suggestion
|
||||
dotnet_diagnostic.SA1202.severity = suggestion
|
||||
dotnet_diagnostic.SA1204.severity = suggestion
|
||||
dotnet_diagnostic.SA1300.severity = none
|
||||
dotnet_diagnostic.SA1303.severity = none
|
||||
dotnet_diagnostic.SA1304.severity = none
|
||||
dotnet_diagnostic.SA1306.severity = none
|
||||
dotnet_diagnostic.SA1309.severity = none
|
||||
dotnet_diagnostic.SA1310.severity = none
|
||||
dotnet_diagnostic.SA1401.severity = none
|
||||
dotnet_diagnostic.SA1402.severity = none
|
||||
dotnet_diagnostic.SA1404.severity = suggestion
|
||||
dotnet_diagnostic.SA1405.severity = suggestion
|
||||
dotnet_diagnostic.SA1406.severity = suggestion
|
||||
dotnet_diagnostic.SA1410.severity = suggestion
|
||||
dotnet_diagnostic.SA1411.severity = suggestion
|
||||
dotnet_diagnostic.SA1413.severity = none
|
||||
dotnet_diagnostic.SA1516.severity = none
|
||||
dotnet_diagnostic.SA1600.severity = none
|
||||
dotnet_diagnostic.SA1601.severity = none
|
||||
dotnet_diagnostic.SA1602.severity = none
|
||||
dotnet_diagnostic.SA1604.severity = none
|
||||
dotnet_diagnostic.SA1605.severity = none
|
||||
dotnet_diagnostic.SA1606.severity = none
|
||||
dotnet_diagnostic.SA1607.severity = none
|
||||
dotnet_diagnostic.SA1608.severity = none
|
||||
dotnet_diagnostic.SA1610.severity = none
|
||||
dotnet_diagnostic.SA1611.severity = none
|
||||
dotnet_diagnostic.SA1612.severity = none
|
||||
dotnet_diagnostic.SA1613.severity = none
|
||||
dotnet_diagnostic.SA1614.severity = none
|
||||
dotnet_diagnostic.SA1615.severity = none
|
||||
dotnet_diagnostic.SA1616.severity = none
|
||||
dotnet_diagnostic.SA1617.severity = none
|
||||
dotnet_diagnostic.SA1618.severity = none
|
||||
dotnet_diagnostic.SA1619.severity = none
|
||||
dotnet_diagnostic.SA1620.severity = none
|
||||
dotnet_diagnostic.SA1621.severity = none
|
||||
dotnet_diagnostic.SA1622.severity = none
|
||||
dotnet_diagnostic.SA1623.severity = none
|
||||
dotnet_diagnostic.SA1624.severity = none
|
||||
dotnet_diagnostic.SA1625.severity = none
|
||||
dotnet_diagnostic.SA1626.severity = none
|
||||
dotnet_diagnostic.SA1627.severity = none
|
||||
dotnet_diagnostic.SA1629.severity = none
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
dotnet_diagnostic.SA1634.severity = none
|
||||
dotnet_diagnostic.SA1635.severity = none
|
||||
dotnet_diagnostic.SA1636.severity = none
|
||||
dotnet_diagnostic.SA1637.severity = none
|
||||
dotnet_diagnostic.SA1638.severity = none
|
||||
dotnet_diagnostic.SA1640.severity = none
|
||||
dotnet_diagnostic.SA1641.severity = none
|
||||
dotnet_diagnostic.SA1642.severity = none
|
||||
dotnet_diagnostic.SA1643.severity = none
|
||||
dotnet_diagnostic.SA1648.severity = none
|
||||
dotnet_diagnostic.SA1649.severity = none
|
||||
dotnet_diagnostic.SA1651.severity = none
|
||||
dotnet_diagnostic.SX1309.severity = warning
|
||||
|
||||
# Microsoft Analyzers that fail and need to be sorted thru
|
||||
dotnet_diagnostic.ASP0000.severity = suggestion
|
||||
dotnet_diagnostic.CA1000.severity = suggestion
|
||||
dotnet_diagnostic.CA1001.severity = suggestion
|
||||
dotnet_diagnostic.CA1002.severity = suggestion
|
||||
dotnet_diagnostic.CA1003.severity = suggestion
|
||||
dotnet_diagnostic.CA1008.severity = suggestion
|
||||
dotnet_diagnostic.CA1010.severity = suggestion
|
||||
dotnet_diagnostic.CA1012.severity = suggestion
|
||||
dotnet_diagnostic.CA1014.severity = suggestion
|
||||
dotnet_diagnostic.CA1016.severity = suggestion
|
||||
dotnet_diagnostic.CA1017.severity = suggestion
|
||||
dotnet_diagnostic.CA1018.severity = suggestion
|
||||
dotnet_diagnostic.CA1019.severity = suggestion
|
||||
dotnet_diagnostic.CA1021.severity = suggestion
|
||||
dotnet_diagnostic.CA1024.severity = suggestion
|
||||
dotnet_diagnostic.CA1027.severity = suggestion
|
||||
dotnet_diagnostic.CA1028.severity = suggestion
|
||||
dotnet_diagnostic.CA1030.severity = suggestion
|
||||
dotnet_diagnostic.CA1031.severity = suggestion
|
||||
dotnet_diagnostic.CA1032.severity = suggestion
|
||||
dotnet_diagnostic.CA1033.severity = suggestion
|
||||
dotnet_diagnostic.CA1034.severity = suggestion
|
||||
dotnet_diagnostic.CA1036.severity = suggestion
|
||||
dotnet_diagnostic.CA1040.severity = suggestion
|
||||
dotnet_diagnostic.CA1041.severity = suggestion
|
||||
dotnet_diagnostic.CA1043.severity = suggestion
|
||||
dotnet_diagnostic.CA1044.severity = suggestion
|
||||
dotnet_diagnostic.CA1050.severity = suggestion
|
||||
dotnet_diagnostic.CA1051.severity = suggestion
|
||||
dotnet_diagnostic.CA1052.severity = suggestion
|
||||
dotnet_diagnostic.CA1054.severity = suggestion
|
||||
dotnet_diagnostic.CA1055.severity = suggestion
|
||||
dotnet_diagnostic.CA1056.severity = suggestion
|
||||
dotnet_diagnostic.CA1058.severity = suggestion
|
||||
dotnet_diagnostic.CA1060.severity = suggestion
|
||||
dotnet_diagnostic.CA1061.severity = suggestion
|
||||
dotnet_diagnostic.CA1062.severity = suggestion
|
||||
dotnet_diagnostic.CA1063.severity = suggestion
|
||||
dotnet_diagnostic.CA1064.severity = suggestion
|
||||
dotnet_diagnostic.CA1065.severity = suggestion
|
||||
dotnet_diagnostic.CA1066.severity = suggestion
|
||||
dotnet_diagnostic.CA1067.severity = suggestion
|
||||
dotnet_diagnostic.CA1068.severity = suggestion
|
||||
dotnet_diagnostic.CA1069.severity = suggestion
|
||||
dotnet_diagnostic.CA1200.severity = suggestion
|
||||
dotnet_diagnostic.CA1303.severity = suggestion
|
||||
dotnet_diagnostic.CA1304.severity = suggestion
|
||||
dotnet_diagnostic.CA1305.severity = suggestion
|
||||
dotnet_diagnostic.CA1307.severity = suggestion
|
||||
dotnet_diagnostic.CA1308.severity = suggestion
|
||||
dotnet_diagnostic.CA1309.severity = suggestion
|
||||
dotnet_diagnostic.CA1310.severity = suggestion
|
||||
dotnet_diagnostic.CA1401.severity = suggestion
|
||||
dotnet_diagnostic.CA1416.severity = suggestion
|
||||
dotnet_diagnostic.CA1507.severity = suggestion
|
||||
dotnet_diagnostic.CA1508.severity = suggestion
|
||||
dotnet_diagnostic.CA1707.severity = suggestion
|
||||
dotnet_diagnostic.CA1708.severity = suggestion
|
||||
dotnet_diagnostic.CA1710.severity = suggestion
|
||||
dotnet_diagnostic.CA1711.severity = suggestion
|
||||
dotnet_diagnostic.CA1712.severity = suggestion
|
||||
dotnet_diagnostic.CA1714.severity = suggestion
|
||||
dotnet_diagnostic.CA1715.severity = suggestion
|
||||
dotnet_diagnostic.CA1716.severity = suggestion
|
||||
dotnet_diagnostic.CA1717.severity = suggestion
|
||||
dotnet_diagnostic.CA1720.severity = suggestion
|
||||
dotnet_diagnostic.CA1721.severity = suggestion
|
||||
dotnet_diagnostic.CA1724.severity = suggestion
|
||||
dotnet_diagnostic.CA1725.severity = suggestion
|
||||
dotnet_diagnostic.CA1801.severity = suggestion
|
||||
dotnet_diagnostic.CA1802.severity = suggestion
|
||||
dotnet_diagnostic.CA1805.severity = suggestion
|
||||
dotnet_diagnostic.CA1806.severity = suggestion
|
||||
dotnet_diagnostic.CA1810.severity = suggestion
|
||||
dotnet_diagnostic.CA1812.severity = suggestion
|
||||
dotnet_diagnostic.CA1813.severity = suggestion
|
||||
dotnet_diagnostic.CA1814.severity = suggestion
|
||||
dotnet_diagnostic.CA1815.severity = suggestion
|
||||
dotnet_diagnostic.CA1816.severity = suggestion
|
||||
dotnet_diagnostic.CA1819.severity = suggestion
|
||||
dotnet_diagnostic.CA1822.severity = suggestion
|
||||
dotnet_diagnostic.CA1823.severity = suggestion
|
||||
dotnet_diagnostic.CA1824.severity = suggestion
|
||||
dotnet_diagnostic.CA2000.severity = suggestion
|
||||
dotnet_diagnostic.CA2002.severity = suggestion
|
||||
dotnet_diagnostic.CA2007.severity = suggestion
|
||||
dotnet_diagnostic.CA2008.severity = suggestion
|
||||
dotnet_diagnostic.CA2009.severity = suggestion
|
||||
dotnet_diagnostic.CA2010.severity = suggestion
|
||||
dotnet_diagnostic.CA2011.severity = suggestion
|
||||
dotnet_diagnostic.CA2012.severity = suggestion
|
||||
dotnet_diagnostic.CA2013.severity = suggestion
|
||||
dotnet_diagnostic.CA2100.severity = suggestion
|
||||
dotnet_diagnostic.CA2101.severity = suggestion
|
||||
dotnet_diagnostic.CA2119.severity = suggestion
|
||||
dotnet_diagnostic.CA2153.severity = suggestion
|
||||
dotnet_diagnostic.CA2200.severity = suggestion
|
||||
dotnet_diagnostic.CA2201.severity = suggestion
|
||||
dotnet_diagnostic.CA2207.severity = suggestion
|
||||
dotnet_diagnostic.CA2208.severity = suggestion
|
||||
dotnet_diagnostic.CA2211.severity = suggestion
|
||||
dotnet_diagnostic.CA2213.severity = suggestion
|
||||
dotnet_diagnostic.CA2214.severity = suggestion
|
||||
dotnet_diagnostic.CA2215.severity = suggestion
|
||||
dotnet_diagnostic.CA2216.severity = suggestion
|
||||
dotnet_diagnostic.CA2219.severity = suggestion
|
||||
dotnet_diagnostic.CA2225.severity = suggestion
|
||||
dotnet_diagnostic.CA2226.severity = suggestion
|
||||
dotnet_diagnostic.CA2227.severity = suggestion
|
||||
dotnet_diagnostic.CA2229.severity = suggestion
|
||||
dotnet_diagnostic.CA2231.severity = suggestion
|
||||
dotnet_diagnostic.CA2234.severity = suggestion
|
||||
dotnet_diagnostic.CA2235.severity = suggestion
|
||||
dotnet_diagnostic.CA2237.severity = suggestion
|
||||
dotnet_diagnostic.CA2241.severity = suggestion
|
||||
dotnet_diagnostic.CA2242.severity = suggestion
|
||||
dotnet_diagnostic.CA2243.severity = suggestion
|
||||
dotnet_diagnostic.CA2244.severity = suggestion
|
||||
dotnet_diagnostic.CA2245.severity = suggestion
|
||||
dotnet_diagnostic.CA2246.severity = suggestion
|
||||
dotnet_diagnostic.CA3061.severity = suggestion
|
||||
dotnet_diagnostic.CA3075.severity = suggestion
|
||||
dotnet_diagnostic.CA3076.severity = suggestion
|
||||
dotnet_diagnostic.CA3077.severity = suggestion
|
||||
dotnet_diagnostic.CA3147.severity = suggestion
|
||||
dotnet_diagnostic.CA5350.severity = suggestion
|
||||
dotnet_diagnostic.CA5351.severity = suggestion
|
||||
dotnet_diagnostic.CA5359.severity = suggestion
|
||||
dotnet_diagnostic.CA5360.severity = suggestion
|
||||
dotnet_diagnostic.CA5363.severity = suggestion
|
||||
dotnet_diagnostic.CA5364.severity = suggestion
|
||||
dotnet_diagnostic.CA5365.severity = suggestion
|
||||
dotnet_diagnostic.CA5366.severity = suggestion
|
||||
dotnet_diagnostic.CA5368.severity = suggestion
|
||||
dotnet_diagnostic.CA5369.severity = suggestion
|
||||
dotnet_diagnostic.CA5370.severity = suggestion
|
||||
dotnet_diagnostic.CA5371.severity = suggestion
|
||||
dotnet_diagnostic.CA5372.severity = suggestion
|
||||
dotnet_diagnostic.CA5373.severity = suggestion
|
||||
dotnet_diagnostic.CA5374.severity = suggestion
|
||||
dotnet_diagnostic.CA5379.severity = suggestion
|
||||
dotnet_diagnostic.CA5384.severity = suggestion
|
||||
dotnet_diagnostic.CA5385.severity = suggestion
|
||||
dotnet_diagnostic.CA5392.severity = suggestion
|
||||
dotnet_diagnostic.CA5394.severity = suggestion
|
||||
dotnet_diagnostic.CA5397.severity = suggestion
|
||||
|
||||
dotnet_diagnostic.SYSLIB0006.severity = none
|
||||
dotnet_diagnostic.SYSLIB0014.severity = none
|
||||
|
||||
[*.{js,html,js,hbs,less,css}]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -117,12 +117,15 @@ src/UI/.idea/*
|
||||
*log.txt
|
||||
node_modules/
|
||||
_output*
|
||||
_artifacts
|
||||
_rawPackage/
|
||||
_dotTrace*
|
||||
_tests*
|
||||
_publish*
|
||||
_temp*
|
||||
*.Result.xml
|
||||
coverage*.xml
|
||||
coverage*.json
|
||||
setup/Output/
|
||||
*.~is
|
||||
|
||||
@ -133,12 +136,28 @@ bin
|
||||
obj
|
||||
output/*
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# Packages
|
||||
Sonarr_*/
|
||||
Sonarr_*.zip
|
||||
Sonarr_*.gz
|
||||
gecko.zip
|
||||
geckodriver.exe
|
||||
|
||||
#OS X metadata files
|
||||
._*
|
||||
.DS_Store
|
||||
|
||||
_start
|
||||
_temp_*/**/*
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
|
||||
src/.idea/
|
||||
/distribution/windows/setup/output/*
|
||||
|
591
build.sh
591
build.sh
@ -1,122 +1,49 @@
|
||||
#! /bin/bash
|
||||
msBuildVersion='15.0'
|
||||
outputFolder='./_output'
|
||||
outputFolderWindows='./_output_windows'
|
||||
outputFolderLinux='./_output_linux'
|
||||
outputFolderMacOS='./_output_macos'
|
||||
outputFolderMacOSApp='./_output_macos_app'
|
||||
testPackageFolder='./_tests'
|
||||
testPackageFolderWindows='./_tests_windows'
|
||||
testPackageFolderLinux='./_tests_linux'
|
||||
sourceFolder='./src'
|
||||
slnFile=$sourceFolder/Sonarr.sln
|
||||
updateSubFolder=Sonarr.Update
|
||||
#! /usr/bin/env bash
|
||||
set -e
|
||||
|
||||
nuget='tools/nuget/nuget.exe';
|
||||
vswhere='tools/vswhere/vswhere.exe';
|
||||
|
||||
. ./version.sh
|
||||
|
||||
CheckExitCode()
|
||||
{
|
||||
"$@"
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
echo "error with $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
return $status
|
||||
}
|
||||
outputFolder='_output'
|
||||
testPackageFolder='_tests'
|
||||
artifactsFolder="_artifacts";
|
||||
|
||||
ProgressStart()
|
||||
{
|
||||
echo "##teamcity[blockOpened name='$1']"
|
||||
echo "##teamcity[progressStart '$1']"
|
||||
echo "Start '$1'"
|
||||
}
|
||||
|
||||
ProgressEnd()
|
||||
{
|
||||
echo "##teamcity[progressFinish '$1']"
|
||||
echo "##teamcity[blockClosed name='$1']"
|
||||
echo "Finish '$1'"
|
||||
}
|
||||
|
||||
UpdateVersionNumber()
|
||||
{
|
||||
if [ "$BUILD_NUMBER" != "" ]; then
|
||||
if [ "$SONARRVERSION" != "" ]; then
|
||||
echo "Updating Version Info"
|
||||
verMajorMinorRevision=`echo "$buildVersion" | cut -d. -f1,2,3`
|
||||
verBuild=`echo "${BUILD_NUMBER}" | cut -d. -f4`
|
||||
BUILD_NUMBER=$verMajorMinorRevision.$verBuild
|
||||
echo "##teamcity[buildNumber '$BUILD_NUMBER']"
|
||||
sed -i "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$BUILD_NUMBER<\/AssemblyVersion>/g" ./src/Directory.Build.props
|
||||
sed -i "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BRANCH:-dev}<\/AssemblyConfiguration>/g" ./src/Directory.Build.props
|
||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$SONARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$SONARRVERSION<\/string>/g" distribution/osx/Sonarr.app/Contents/Info.plist
|
||||
fi
|
||||
}
|
||||
|
||||
CreateReleaseInfo()
|
||||
EnableBsdSupport()
|
||||
{
|
||||
if [ "$BUILD_NUMBER" != "" ]; then
|
||||
echo "Create Release Info"
|
||||
echo -e "# Do Not Edit\nReleaseVersion=$BUILD_NUMBER\nBranch=${BRANCH:-dev}" > $outputFolder/release_info
|
||||
#todo enable sdk with
|
||||
#SDK_PATH=$(dotnet --list-sdks | grep -P '5\.\d\.\d+' | head -1 | sed 's/\(5\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g')
|
||||
# BUNDLED_VERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||
|
||||
if grep -qv freebsd-x64 src/Directory.Build.props; then
|
||||
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64</RuntimeIdentifiers>^g" src/Directory.Build.props
|
||||
fi
|
||||
}
|
||||
|
||||
CleanFolder()
|
||||
{
|
||||
local path=$1
|
||||
local keepConfigFiles=$2
|
||||
|
||||
find $path -name "*.transform" -exec rm "{}" \;
|
||||
|
||||
if [ $keepConfigFiles != true ] ; then
|
||||
find $path -name "*.dll.config" -exec rm "{}" \;
|
||||
fi
|
||||
|
||||
echo "Removing FluentValidation.Resources files"
|
||||
find $path -name "FluentValidation.resources.dll" -exec rm "{}" \;
|
||||
find $path -name "App.config" -exec rm "{}" \;
|
||||
|
||||
echo "Removing vshost files"
|
||||
find $path -name "*.vshost.exe" -exec rm "{}" \;
|
||||
|
||||
echo "Removing dylib files"
|
||||
find $path -name "*.dylib" -exec rm "{}" \;
|
||||
|
||||
echo "Removing Empty folders"
|
||||
find $path -depth -empty -type d -exec rm -r "{}" \;
|
||||
}
|
||||
|
||||
BuildWithMSBuild()
|
||||
{
|
||||
msBuildPath=`$vswhere -latest -products \* -requires Microsoft.Component.MSBuild -find MSBuild\\\\\*\*\\\\Bin\\\\MSBuild.exe`
|
||||
msBuildPath=${msBuildPath/C:\\/\/c\/}
|
||||
msBuildPath=${msBuildPath//\\/\/}
|
||||
msBuildDir=$(dirname "$msBuildPath")
|
||||
|
||||
echo $msBuildDir
|
||||
|
||||
export PATH=$msBuildDir:$PATH
|
||||
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Clean //m
|
||||
$nuget restore $slnFile
|
||||
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Build //m //p:AllowedReferenceRelatedFileExtensions=.pdb
|
||||
}
|
||||
|
||||
BuildWithXbuild()
|
||||
{
|
||||
export MONO_IOMAP=case
|
||||
CheckExitCode msbuild /t:Clean $slnFile
|
||||
mono $nuget restore $slnFile
|
||||
CheckExitCode msbuild /p:Configuration=Release /p:Platform=x86 /t:Build /p:AllowedReferenceRelatedFileExtensions=.pdb $slnFile
|
||||
}
|
||||
|
||||
LintUI()
|
||||
{
|
||||
ProgressStart 'ESLint'
|
||||
CheckExitCode yarn lint
|
||||
yarn lint
|
||||
ProgressEnd 'ESLint'
|
||||
|
||||
ProgressStart 'Stylelint'
|
||||
CheckExitCode yarn stylelint
|
||||
yarn stylelint
|
||||
ProgressEnd 'Stylelint'
|
||||
}
|
||||
|
||||
@ -127,331 +54,323 @@ Build()
|
||||
rm -rf $outputFolder
|
||||
rm -rf $testPackageFolder
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
BuildWithMSBuild
|
||||
slnFile=src/Sonarr.sln
|
||||
|
||||
if [ $os = "windows" ]; then
|
||||
platform=Windows
|
||||
else
|
||||
BuildWithXbuild
|
||||
platform=Posix
|
||||
fi
|
||||
|
||||
CleanFolder $outputFolder false
|
||||
dotnet clean $slnFile -c Debug
|
||||
dotnet clean $slnFile -c Release
|
||||
|
||||
echo "Removing Mono.Posix.dll"
|
||||
rm $outputFolder/Mono.Posix.dll
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
|
||||
else
|
||||
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$RID -t:PublishAllRids
|
||||
fi
|
||||
|
||||
ProgressEnd 'Build'
|
||||
}
|
||||
|
||||
RunWebpack()
|
||||
YarnInstall()
|
||||
{
|
||||
ProgressStart 'yarn install'
|
||||
yarn install
|
||||
yarn install --frozen-lockfile --network-timeout 120000
|
||||
ProgressEnd 'yarn install'
|
||||
}
|
||||
|
||||
LintUI
|
||||
|
||||
RunWebpack()
|
||||
{
|
||||
ProgressStart 'Running webpack'
|
||||
CheckExitCode yarn run build --env production
|
||||
yarn run build --env production
|
||||
ProgressEnd 'Running webpack'
|
||||
}
|
||||
|
||||
CreateMdbs()
|
||||
PackageFiles()
|
||||
{
|
||||
local path=$1
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
local pdbFiles=( $(find $path -name "*.pdb") )
|
||||
for filename in "${pdbFiles[@]}"
|
||||
do
|
||||
if [ -e ${filename%.pdb}.dll ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.dll
|
||||
fi
|
||||
if [ -e ${filename%.pdb}.exe ] ; then
|
||||
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.exe
|
||||
fi
|
||||
done
|
||||
fi
|
||||
local folder="$1"
|
||||
local framework="$2"
|
||||
local runtime="$3"
|
||||
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r $outputFolder/$framework/$runtime/publish/* $folder
|
||||
cp -r $outputFolder/Sonarr.Update/$framework/$runtime/publish $folder/Sonarr.Update
|
||||
cp -r $outputFolder/UI $folder
|
||||
|
||||
echo "Adding LICENSE"
|
||||
cp LICENSE.md $folder
|
||||
}
|
||||
|
||||
PatchMono()
|
||||
PackageLinux()
|
||||
{
|
||||
local path=$1
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
# Below we deal with some mono incompatibilities with windows-only dotnet core/standard libs
|
||||
# See: https://github.com/mono/mono/blob/master/tools/nuget-hash-extractor/download.sh
|
||||
# That list defines assemblies that are prohibited from being loaded from the appdir, instead loading from mono GAC.
|
||||
ProgressStart "Creating $runtime Package for $framework"
|
||||
|
||||
# We have debian dependencies to get these installed or facades from mono 5.10+
|
||||
for assembly in System.IO.Compression System.Runtime.InteropServices.RuntimeInformation System.Net.Http System.Globalization.Extensions System.Text.Encoding.CodePages System.Threading.Overlapped
|
||||
do
|
||||
if [ -e $path/$assembly.dll ]; then
|
||||
if [ -e $sourceFolder/Libraries/Mono/$assembly.dll ]; then
|
||||
echo "Copy Mono-specific facade $assembly.dll (uses win32 interop)"
|
||||
cp $sourceFolder/Libraries/Mono/$assembly.dll $path/$assembly.dll
|
||||
else
|
||||
echo "Remove $assembly.dll (uses win32 interop)"
|
||||
rm $path/$assembly.dll
|
||||
fi
|
||||
local folder=$artifactsFolder/$runtime/$framework/Sonarr
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy more stable version of Vectors for mono <5.12
|
||||
if [ -e $path/System.Numerics.Vectors.dll ]; then
|
||||
packageDir="$HOME/.nuget/packages/system.numerics.vectors/4.5.0"
|
||||
|
||||
if [ ! -d "$HOME/.nuget/packages/system.numerics.vectors/4.5.0" ]; then
|
||||
# May reside in the NuGetFallback folder, which is harder to find
|
||||
# Download somewhere to get the real cache populated
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install System.Numerics.Vectors -Version 4.5.0 -Output ./_temp/System.Numerics.Vectors
|
||||
else
|
||||
mono $nuget install System.Numerics.Vectors -Version 4.5.0 -Output ./_temp/System.Numerics.Vectors
|
||||
fi
|
||||
rm -rf ./_temp/System.Numerics.Vectors
|
||||
fi
|
||||
# Copy the netstandard2.0 version rather than net46
|
||||
cp "$packageDir/lib/netstandard2.0/System.Numerics.Vectors.dll" $path/
|
||||
fi
|
||||
}
|
||||
|
||||
PackageMono()
|
||||
{
|
||||
ProgressStart 'Creating Mono Package'
|
||||
|
||||
rm -rf $outputFolderLinux
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $outputFolder $outputFolderLinux
|
||||
|
||||
echo "Creating MDBs"
|
||||
CreateMdbs $outputFolderLinux
|
||||
|
||||
echo "Removing PDBs"
|
||||
find $outputFolderLinux -name "*.pdb" -exec rm "{}" \;
|
||||
PackageFiles "$folder" "$framework" "$runtime"
|
||||
|
||||
echo "Removing Service helpers"
|
||||
rm -f $outputFolderLinux/ServiceUninstall.*
|
||||
rm -f $outputFolderLinux/ServiceInstall.*
|
||||
|
||||
echo "Removing native windows binaries Sqlite, MediaInfo"
|
||||
rm -f $outputFolderLinux/sqlite3.*
|
||||
rm -f $outputFolderLinux/MediaInfo.*
|
||||
|
||||
PatchMono $outputFolderLinux
|
||||
|
||||
echo "Adding Sonarr.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/Sonarr.Core.dll.config $outputFolderLinux
|
||||
|
||||
# Remove Http binding redirect by renaming it
|
||||
# We don't need this anymore once our minimum mono version is 5.10
|
||||
sed -i "s/System.Net.Http/System.Net.Http.Mono/g" $outputFolderLinux/Sonarr.Console.exe.config
|
||||
|
||||
echo "Renaming Sonarr.Console.exe to Sonarr.exe"
|
||||
rm $outputFolderLinux/Sonarr.exe*
|
||||
for file in $outputFolderLinux/Sonarr.Console.exe*; do
|
||||
mv "$file" "${file//.Console/}"
|
||||
done
|
||||
rm -f $folder/ServiceUninstall.*
|
||||
rm -f $folder/ServiceInstall.*
|
||||
|
||||
echo "Removing Sonarr.Windows"
|
||||
rm $outputFolderLinux/Sonarr.Windows.*
|
||||
rm $folder/Sonarr.Windows.*
|
||||
|
||||
echo "Adding Sonarr.Mono to UpdatePackage"
|
||||
cp $outputFolderLinux/Sonarr.Mono.* $outputFolderLinux/$updateSubFolder/
|
||||
cp $folder/Sonarr.Mono.* $folder/Sonarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Sonarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Sonarr.Update
|
||||
fi
|
||||
|
||||
ProgressEnd 'Creating Mono Package'
|
||||
ProgressEnd "Creating $runtime Package for $framework"
|
||||
}
|
||||
|
||||
PackageMacOS()
|
||||
{
|
||||
ProgressStart 'Creating MacOS Package'
|
||||
local framework="$1"
|
||||
|
||||
rm -rf $outputFolderMacOS
|
||||
mkdir $outputFolderMacOS
|
||||
ProgressStart "Creating MacOS Package for $framework"
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $outputFolderLinux/* $outputFolderMacOS
|
||||
local folder=$artifactsFolder/macos/$framework/Sonarr
|
||||
|
||||
echo "Adding Sonarr Launcher"
|
||||
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/
|
||||
mv $outputFolderMacOS/Sonarr.exe $outputFolderMacOS/Sonarr.exe.bak
|
||||
mv $outputFolderMacOS/Launcher $outputFolderMacOS/Sonarr
|
||||
mv $outputFolderMacOS/Sonarr.exe.bak $outputFolderMacOS/Sonarr.exe
|
||||
chmod +x $outputFolderMacOS/Sonarr
|
||||
PackageFiles "$folder" "$framework" "osx-x64"
|
||||
|
||||
echo "Adding Sonarr.Update Launcher"
|
||||
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOS/Sonarr.Update/
|
||||
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak
|
||||
mv $outputFolderMacOS/Sonarr.Update/Launcher $outputFolderMacOS/Sonarr.Update/Sonarr.Update
|
||||
mv $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe.bak $outputFolderMacOS/Sonarr.Update/Sonarr.Update.exe
|
||||
chmod +x $outputFolderMacOS/Sonarr.Update/Sonarr.Update
|
||||
echo "Removing Service helpers"
|
||||
rm -f $folder/ServiceUninstall.*
|
||||
rm -f $folder/ServiceInstall.*
|
||||
|
||||
echo "Adding sqlite dylibs"
|
||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOS
|
||||
echo "Removing Sonarr.Windows"
|
||||
rm $folder/Sonarr.Windows.*
|
||||
|
||||
echo "Adding MediaInfo dylib"
|
||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOS
|
||||
echo "Adding Sonarr.Mono to UpdatePackage"
|
||||
cp $folder/Sonarr.Mono.* $folder/Sonarr.Update
|
||||
if [ "$framework" = "net6.0" ]; then
|
||||
cp $folder/Mono.Posix.NETStandard.* $folder/Sonarr.Update
|
||||
cp $folder/libMonoPosixHelper.* $folder/Sonarr.Update
|
||||
fi
|
||||
|
||||
ProgressEnd 'Creating MacOS Package'
|
||||
}
|
||||
|
||||
PackageMacOSApp()
|
||||
{
|
||||
ProgressStart 'Creating macOS App Package'
|
||||
local framework="$1"
|
||||
|
||||
rm -rf $outputFolderMacOSApp
|
||||
mkdir $outputFolderMacOSApp
|
||||
cp -r ./distribution/osx/Sonarr.app $outputFolderMacOSApp
|
||||
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS
|
||||
ProgressStart "Creating macOS App Package for $framework"
|
||||
|
||||
echo "Adding Sonarr Launcher"
|
||||
cp ./distribution/osx/Launcher/dist/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/
|
||||
mv $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Launcher $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr
|
||||
chmod +x $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/Sonarr
|
||||
local folder=$artifactsFolder/macos-app/$framework
|
||||
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r distribution/osx/Sonarr.app $folder
|
||||
mkdir -p $folder/Sonarr.app/Contents/MacOS
|
||||
|
||||
echo "Copying Binaries"
|
||||
mkdir -p $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin
|
||||
cp -r $outputFolderLinux/* $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||
|
||||
echo "Adding sqlite dylibs"
|
||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||
|
||||
echo "Adding MediaInfo dylib"
|
||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/
|
||||
cp -r $artifactsFolder/macos/$framework/Sonarr/* $folder/Sonarr.app/Contents/MacOS
|
||||
|
||||
echo "Removing Update Folder"
|
||||
rm -r $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/bin/Sonarr.Update
|
||||
echo "# Do Not Edit\nPackageVersion=${BUILD_NUMBER}\nPackageAuthor=[Team Sonarr](https://sonarr.tv)\nReleaseVersion=${BUILD_NUMBER}\nUpdateMethod=$PackageUpdater\nBranch=${Branch:-master}" > $outputFolderMacOSApp/Sonarr.app/Contents/MacOS/package_info
|
||||
rm -r $folder/Sonarr.app/Contents/MacOS/Sonarr.Update
|
||||
|
||||
ProgressEnd 'Creating macOS App Package'
|
||||
}
|
||||
|
||||
PackageTestsMono()
|
||||
{
|
||||
ProgressStart 'Creating Mono Test Package'
|
||||
|
||||
rm -rf $testPackageFolderLinux
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $testPackageFolder $testPackageFolderLinux
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install NUnit.ConsoleRunner -Version 3.10.0 -Output $testPackageFolderLinux
|
||||
else
|
||||
mono $nuget install NUnit.ConsoleRunner -Version 3.10.0 -Output $testPackageFolderLinux
|
||||
fi
|
||||
|
||||
echo "Creating MDBs"
|
||||
CreateMdbs $testPackageFolderLinux
|
||||
|
||||
echo "Removing PDBs"
|
||||
find $testPackageFolderLinux -name "*.pdb" -exec rm "{}" \;
|
||||
|
||||
PatchMono $testPackageFolderLinux
|
||||
|
||||
echo "Adding Sonarr.Core.dll.config (for dllmap)"
|
||||
cp $sourceFolder/NzbDrone.Core/Sonarr.Core.dll.config $testPackageFolderLinux
|
||||
|
||||
# Remove Http binding redirect by renaming it
|
||||
# We don't need this anymore once our minimum mono version is 5.10
|
||||
sed -i "s/System.Net.Http/System.Net.Http.Mono/g" $testPackageFolderLinux/Sonarr.Common.Test.dll.config
|
||||
|
||||
cp ./test.sh $testPackageFolderLinux/
|
||||
dos2unix $testPackageFolderLinux/test.sh
|
||||
|
||||
echo "Removing Sonarr.Windows"
|
||||
rm $testPackageFolderLinux/Sonarr.Windows.*
|
||||
|
||||
rm -f $testPackageFolderLinux/*.log.config
|
||||
|
||||
CleanFolder $testPackageFolderLinux true
|
||||
|
||||
ProgressEnd 'Creating Linux Test Package'
|
||||
}
|
||||
|
||||
PackageTestsWindows()
|
||||
{
|
||||
ProgressStart 'Creating Windows Test Package'
|
||||
|
||||
rm -rf $testPackageFolderWindows
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $testPackageFolder $testPackageFolderWindows
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install NUnit.ConsoleRunner -Version 3.10.0 -Output $testPackageFolderWindows
|
||||
else
|
||||
mono $nuget install NUnit.ConsoleRunner -Version 3.10.0 -Output $testPackageFolderWindows
|
||||
fi
|
||||
|
||||
cp ./test.sh $testPackageFolderWindows
|
||||
|
||||
echo "Removing Sonarr.Mono"
|
||||
rm -f $testPackageFolderWindows/Sonarr.Mono.*
|
||||
|
||||
rm -f $testPackageFolderWindows/*.log.config
|
||||
|
||||
CleanFolder $testPackageFolderWindows true
|
||||
|
||||
ProgressEnd 'Creating Windows Test Package'
|
||||
}
|
||||
|
||||
PackageWindows()
|
||||
{
|
||||
ProgressStart 'Creating Windows Package'
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
rm -rf $outputFolderWindows
|
||||
ProgressStart "Creating Windows Package for $framework"
|
||||
|
||||
echo "Copying Binaries"
|
||||
cp -r $outputFolder $outputFolderWindows
|
||||
local folder=$artifactsFolder/$runtime/$framework/Sonarr
|
||||
|
||||
PackageFiles "$folder" "$framework" "$runtime"
|
||||
cp -r $outputFolder/$framework-windows/$runtime/publish/* $folder
|
||||
|
||||
echo "Removing Sonarr.Mono"
|
||||
rm -f $outputFolderWindows/Sonarr.Mono.*
|
||||
rm -f $folder/Sonarr.Mono.*
|
||||
rm -f $folder/Mono.Posix.NETStandard.*
|
||||
rm -f $folder/libMonoPosixHelper.*
|
||||
|
||||
echo "Adding Sonarr.Windows to UpdatePackage"
|
||||
cp $outputFolderWindows/Sonarr.Windows.* $outputFolderWindows/$updateSubFolder/
|
||||
cp $folder/Sonarr.Windows.* $folder/Sonarr.Update
|
||||
|
||||
ProgressEnd 'Creating Windows Package'
|
||||
}
|
||||
|
||||
PublishArtifacts()
|
||||
Package()
|
||||
{
|
||||
ProgressStart 'Publishing Artifacts'
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
local SPLIT
|
||||
|
||||
# Tests
|
||||
echo "##teamcity[publishArtifacts '$testPackageFolderWindows/** => tests.windows.zip']"
|
||||
echo "##teamcity[publishArtifacts '$testPackageFolderLinux/** => tests.linux.zip']"
|
||||
IFS='-' read -ra SPLIT <<< "$runtime"
|
||||
|
||||
# Releases
|
||||
echo "##teamcity[publishArtifacts '$outputFolderWindows/** => Sonarr.$BRANCH.$BUILD_NUMBER.windows.zip!Sonarr']"
|
||||
echo "##teamcity[publishArtifacts '$outputFolderLinux/** => Sonarr.$BRANCH.$BUILD_NUMBER.linux.tar.gz!Sonarr']"
|
||||
echo "##teamcity[publishArtifacts '$outputFolderMacOS/** => Sonarr.$BRANCH.$BUILD_NUMBER.macos.tar.gz!Sonarr']"
|
||||
echo "##teamcity[publishArtifacts '$outputFolderMacOSApp/** => Sonarr.$BRANCH.$BUILD_NUMBER.macos.zip']"
|
||||
case "${SPLIT[0]}" in
|
||||
linux|freebsd*)
|
||||
PackageLinux "$framework" "$runtime"
|
||||
;;
|
||||
win)
|
||||
PackageWindows "$framework" "$runtime"
|
||||
;;
|
||||
osx)
|
||||
PackageMacOS "$framework"
|
||||
PackageMacOSApp "$framework"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Debian Package
|
||||
echo "##teamcity[publishArtifacts 'distribution/** => distribution.zip']"
|
||||
PackageTests()
|
||||
{
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
ProgressEnd 'Publishing Artifacts'
|
||||
cp test.sh "$testPackageFolder/$framework/$runtime/publish"
|
||||
|
||||
rm -f $testPackageFolder/$framework/$runtime/*.log.config
|
||||
|
||||
ProgressEnd 'Creating Test Package'
|
||||
}
|
||||
|
||||
# Use mono or .net depending on OS
|
||||
case "$(uname -s)" in
|
||||
CYGWIN*|MINGW32*|MINGW64*|MSYS*)
|
||||
# on windows, use dotnet
|
||||
runtime="dotnet"
|
||||
os="windows"
|
||||
;;
|
||||
*)
|
||||
# otherwise use mono
|
||||
runtime="mono"
|
||||
os="posix"
|
||||
;;
|
||||
esac
|
||||
|
||||
UpdateVersionNumber
|
||||
Build
|
||||
CreateReleaseInfo
|
||||
RunWebpack
|
||||
PackageMono
|
||||
PackageMacOS
|
||||
PackageMacOSApp
|
||||
PackageTestsMono
|
||||
PackageTestsWindows
|
||||
PackageWindows
|
||||
PublishArtifacts
|
||||
POSITIONAL=()
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "No arguments provided, building everything"
|
||||
BACKEND=YES
|
||||
FRONTEND=YES
|
||||
PACKAGES=YES
|
||||
LINT=YES
|
||||
ENABLE_BSD=NO
|
||||
fi
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
case $key in
|
||||
--backend)
|
||||
BACKEND=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--enable-bsd)
|
||||
ENABLE_BSD=YES
|
||||
shift # past argument
|
||||
;;
|
||||
-r|--runtime)
|
||||
RID="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
-f|--framework)
|
||||
FRAMEWORK="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--frontend)
|
||||
FRONTEND=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--packages)
|
||||
PACKAGES=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--lint)
|
||||
LINT=YES
|
||||
shift # past argument
|
||||
;;
|
||||
--all)
|
||||
BACKEND=YES
|
||||
FRONTEND=YES
|
||||
PACKAGES=YES
|
||||
LINT=YES
|
||||
shift # past argument
|
||||
;;
|
||||
*) # unknown option
|
||||
POSITIONAL+=("$1") # save it in an array for later
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
set -- "${POSITIONAL[@]}" # restore positional parameters
|
||||
|
||||
if [ "$BACKEND" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
if [ "$ENABLE_BSD" = "YES" ];
|
||||
then
|
||||
EnableBsdSupport
|
||||
fi
|
||||
Build
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
PackageTests "net6.0" "win-x64"
|
||||
PackageTests "net6.0" "win-x86"
|
||||
PackageTests "net6.0" "linux-x64"
|
||||
PackageTests "net6.0" "linux-musl-x64"
|
||||
PackageTests "net6.0" "osx-x64"
|
||||
if [ "$ENABLE_BSD" = "YES" ];
|
||||
then
|
||||
PackageTests "net6.0" "freebsd-x64"
|
||||
fi
|
||||
else
|
||||
PackageTests "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FRONTEND" = "YES" ];
|
||||
then
|
||||
YarnInstall
|
||||
RunWebpack
|
||||
fi
|
||||
|
||||
if [ "$LINT" = "YES" ];
|
||||
then
|
||||
if [ -z "$FRONTEND" ];
|
||||
then
|
||||
YarnInstall
|
||||
fi
|
||||
|
||||
LintUI
|
||||
fi
|
||||
|
||||
if [ "$PACKAGES" = "YES" ];
|
||||
then
|
||||
UpdateVersionNumber
|
||||
|
||||
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
|
||||
then
|
||||
Package "net6.0" "win-x64"
|
||||
Package "net6.0" "win-x86"
|
||||
Package "net6.0" "linux-x64"
|
||||
Package "net6.0" "linux-musl-x64"
|
||||
Package "net6.0" "linux-arm64"
|
||||
Package "net6.0" "linux-musl-arm64"
|
||||
Package "net6.0" "linux-arm"
|
||||
Package "net6.0" "osx-x64"
|
||||
if [ "$ENABLE_BSD" = "YES" ];
|
||||
then
|
||||
Package "net6.0" "freebsd-x64"
|
||||
fi
|
||||
else
|
||||
Package "$FRAMEWORK" "$RID"
|
||||
fi
|
||||
fi
|
||||
|
@ -1,459 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
@ -1,28 +0,0 @@
|
||||
Code reused from duplicati, licensed under LGPL 2.1
|
||||
Modified for Sonarr by Taloth Saldono
|
||||
|
||||
see here for the original source: https://github.com/duplicati/duplicati/tree/679981d29f8a6e445d3c1e6d41e72a673ffaa653/Installer/OSX
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Sonarr as a whole is licensed under GPL 3.0 as specified in the git repository root.
|
||||
|
||||
But to preserve the original intent of the duplicati project, the modified versions of the sources in this folder are dual licensed under LGPL 2.1 and GPL 3.0.
|
||||
Note: This exception can be freely removed in any copy of Sonarr sources as per LGPL/GPL licensing terms.
|
||||
|
||||
A copy of the LGPL 2.1 license is included in the LICENSE.LGPL.md file.
|
||||
|
||||
Purpose
|
||||
-------
|
||||
|
||||
The Launcher is a bootstrap/shim application that checks if the appropriate version of mono is installed and subsequently use it to execute Sonarr.
|
||||
By using a separate application, instead of a shell script, this allows the user to assign certain operating system permissions to Sonarr specifically.
|
||||
|
||||
Compiling the Launcher
|
||||
----------------------
|
||||
|
||||
You need an OSX installation with xcode
|
||||
Then run compile.sh in a terminal
|
||||
|
||||
The generated dist/Launcher can be renamed to Sonarr and Sonarr.Update to serve as shims to run Sonarr.exe and Sonarr.Update.exe respectively.
|
BIN
distribution/osx/Launcher/dist/Launcher
vendored
BIN
distribution/osx/Launcher/dist/Launcher
vendored
Binary file not shown.
@ -1,32 +0,0 @@
|
||||
#import "run-with-mono.h"
|
||||
#import "PFMoveApplication.h"
|
||||
|
||||
int const MONO_VERSION_MAJOR = 5;
|
||||
int const MONO_VERSION_MINOR = 20;
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
// Use our own executable name so the same compiled binary to be used for forks
|
||||
NSString * const FileName = NSProcessInfo.processInfo.arguments[0].lastPathComponent;
|
||||
|
||||
// Sonarr.Update.exe
|
||||
NSString * const ASSEMBLY = [NSString stringWithFormat:@"%@.exe", FileName];
|
||||
|
||||
// Sonarr Update
|
||||
NSString * const APP_NAME = [FileName stringByReplacingOccurrencesOfString:@"." withString:@" "];
|
||||
|
||||
// -sonarrupdate
|
||||
NSString * const PROCESS_NAME = [NSString stringWithFormat:@"-%@", [FileName stringByReplacingOccurrencesOfString:@"." withString:@""].lowercaseString];
|
||||
|
||||
@try
|
||||
{
|
||||
PFMoveToApplicationsFolderIfNecessary();
|
||||
}
|
||||
@catch (NSException * ex)
|
||||
{
|
||||
NSLog(@"Translocation/Quarantine check failed, starting normally. Reason: %@", ex.reason);
|
||||
}
|
||||
|
||||
return [RunWithMono runAssemblyWithMono:APP_NAME procnamesuffix:PROCESS_NAME assembly:ASSEMBLY major:MONO_VERSION_MAJOR minor:MONO_VERSION_MINOR];
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
//
|
||||
// PFMoveApplication.h, version 1.24
|
||||
// LetsMove
|
||||
//
|
||||
// Created by Andy Kim at Potion Factory LLC on 9/17/09
|
||||
//
|
||||
// The contents of this file are dedicated to the public domain.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
Moves the running application to ~/Applications or /Applications if the former does not exist.
|
||||
After the move, it relaunches app from the new location.
|
||||
DOES NOT work for sandboxed applications.
|
||||
|
||||
Call from \c NSApplication's delegate method \c -applicationWillFinishLaunching: method. */
|
||||
void PFMoveToApplicationsFolderIfNecessary(void);
|
||||
|
||||
/**
|
||||
Check whether an app move is currently in progress.
|
||||
Returns YES if LetsMove is currently in-progress trying to move the app to the Applications folder, or NO otherwise.
|
||||
This can be used to work around a crash with apps that terminate after last window is closed.
|
||||
See https://github.com/potionfactory/LetsMove/issues/64 for details. */
|
||||
BOOL PFMoveIsInProgress(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,565 +0,0 @@
|
||||
//
|
||||
// PFMoveApplication.m, version 1.24
|
||||
// LetsMove
|
||||
//
|
||||
// Created by Andy Kim at Potion Factory LLC on 9/17/09
|
||||
//
|
||||
// The contents of this file are dedicated to the public domain.
|
||||
|
||||
#import "PFMoveApplication.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Security/Security.h>
|
||||
#import <dlfcn.h>
|
||||
#import <sys/mount.h>
|
||||
|
||||
@interface LetsMove : NSObject
|
||||
@end
|
||||
|
||||
@implementation LetsMove
|
||||
+ (NSBundle *)bundle {
|
||||
return [NSBundle bundleForClass:self];
|
||||
}
|
||||
@end
|
||||
|
||||
// Strings
|
||||
// These are macros to be able to use custom i18n tools
|
||||
#define _I10NS(nsstr) NSLocalizedStringFromTableInBundle(nsstr, @"MoveApplication", [LetsMove bundle], nil)
|
||||
#define kStrMoveApplicationCouldNotMove _I10NS(@"Could not move to Applications folder")
|
||||
#define kStrMoveApplicationQuestionTitle _I10NS(@"Move to Applications folder?")
|
||||
#define kStrMoveApplicationQuestionTitleHome _I10NS(@"Move to Applications folder in your Home folder?")
|
||||
#define kStrMoveApplicationQuestionMessage _I10NS(@"I can move myself to the Applications folder if you'd like.")
|
||||
#define kStrMoveApplicationButtonMove _I10NS(@"Move to Applications Folder")
|
||||
#define kStrMoveApplicationButtonDoNotMove _I10NS(@"Do Not Move")
|
||||
#define kStrMoveApplicationQuestionInfoWillRequirePasswd _I10NS(@"Note that this will require an administrator password.")
|
||||
#define kStrMoveApplicationQuestionInfoInDownloadsFolder _I10NS(@"This will keep your Downloads folder uncluttered.")
|
||||
|
||||
// Needs to be defined for compiling under 10.5 SDK
|
||||
#ifndef NSAppKitVersionNumber10_5
|
||||
#define NSAppKitVersionNumber10_5 949
|
||||
#endif
|
||||
|
||||
// By default, we use a small control/font for the suppression button.
|
||||
// If you prefer to use the system default (to match your other alerts),
|
||||
// set this to 0.
|
||||
#define PFUseSmallAlertSuppressCheckbox 1
|
||||
|
||||
|
||||
static NSString *AlertSuppressKey = @"moveToApplicationsFolderAlertSuppress";
|
||||
static BOOL MoveInProgress = NO;
|
||||
|
||||
// Helper functions
|
||||
static NSString *PreferredInstallLocation(BOOL *isUserDirectory);
|
||||
static BOOL IsInApplicationsFolder(NSString *path);
|
||||
static BOOL IsInDownloadsFolder(NSString *path);
|
||||
static BOOL IsApplicationAtPathRunning(NSString *path);
|
||||
static BOOL IsApplicationAtPathNested(NSString *path);
|
||||
static NSString *ContainingDiskImageDevice(NSString *path);
|
||||
static BOOL Trash(NSString *path);
|
||||
static BOOL DeleteOrTrash(NSString *path);
|
||||
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled);
|
||||
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath);
|
||||
static NSString *ShellQuotedString(NSString *string);
|
||||
static void Relaunch(NSString *destinationPath);
|
||||
|
||||
// Main worker function
|
||||
void PFMoveToApplicationsFolderIfNecessary(void) {
|
||||
|
||||
// Make sure to do our work on the main thread.
|
||||
// Apparently Electron apps need this for things to work properly.
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
PFMoveToApplicationsFolderIfNecessary();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if user suppressed the alert before
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:AlertSuppressKey]) return;
|
||||
|
||||
// Path of the bundle
|
||||
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
|
||||
|
||||
// Check if the bundle is embedded in another application
|
||||
BOOL isNestedApplication = IsApplicationAtPathNested(bundlePath);
|
||||
|
||||
// Skip if the application is already in some Applications folder,
|
||||
// unless it's inside another app's bundle.
|
||||
if (IsInApplicationsFolder(bundlePath) && !isNestedApplication) return;
|
||||
|
||||
// OK, looks like we'll need to do a move - set the status variable appropriately
|
||||
MoveInProgress = YES;
|
||||
|
||||
// File Manager
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
// Are we on a disk image?
|
||||
NSString *diskImageDevice = ContainingDiskImageDevice(bundlePath);
|
||||
|
||||
// Since we are good to go, get the preferred installation directory.
|
||||
BOOL installToUserApplications = NO;
|
||||
NSString *applicationsDirectory = PreferredInstallLocation(&installToUserApplications);
|
||||
NSString *bundleName = [bundlePath lastPathComponent];
|
||||
NSString *destinationPath = [applicationsDirectory stringByAppendingPathComponent:bundleName];
|
||||
|
||||
// Check if we need admin password to write to the Applications directory
|
||||
BOOL needAuthorization = ([fm isWritableFileAtPath:applicationsDirectory] == NO);
|
||||
|
||||
// Check if the destination bundle is already there but not writable
|
||||
needAuthorization |= ([fm fileExistsAtPath:destinationPath] && ![fm isWritableFileAtPath:destinationPath]);
|
||||
|
||||
// Setup the alert
|
||||
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
|
||||
{
|
||||
NSString *informativeText = nil;
|
||||
|
||||
[alert setMessageText:(installToUserApplications ? kStrMoveApplicationQuestionTitleHome : kStrMoveApplicationQuestionTitle)];
|
||||
|
||||
informativeText = kStrMoveApplicationQuestionMessage;
|
||||
|
||||
if (needAuthorization) {
|
||||
informativeText = [informativeText stringByAppendingString:@" "];
|
||||
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoWillRequirePasswd];
|
||||
}
|
||||
else if (IsInDownloadsFolder(bundlePath)) {
|
||||
// Don't mention this stuff if we need authentication. The informative text is long enough as it is in that case.
|
||||
informativeText = [informativeText stringByAppendingString:@" "];
|
||||
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoInDownloadsFolder];
|
||||
}
|
||||
|
||||
[alert setInformativeText:informativeText];
|
||||
|
||||
// Add accept button
|
||||
[alert addButtonWithTitle:kStrMoveApplicationButtonMove];
|
||||
|
||||
// Add deny button
|
||||
NSButton *cancelButton = [alert addButtonWithTitle:kStrMoveApplicationButtonDoNotMove];
|
||||
[cancelButton setKeyEquivalent:[NSString stringWithFormat:@"%C", 0x1b]]; // Escape key
|
||||
|
||||
// Setup suppression button
|
||||
[alert setShowsSuppressionButton:YES];
|
||||
|
||||
if (PFUseSmallAlertSuppressCheckbox) {
|
||||
NSCell *cell = [[alert suppressionButton] cell];
|
||||
[cell setControlSize:NSSmallControlSize];
|
||||
[cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
|
||||
}
|
||||
}
|
||||
|
||||
// Activate app -- work-around for focus issues related to "scary file from internet" OS dialog.
|
||||
if (![NSApp isActive]) {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn) {
|
||||
NSLog(@"INFO -- Moving myself to the Applications folder");
|
||||
|
||||
// Move
|
||||
if (needAuthorization) {
|
||||
BOOL authorizationCanceled;
|
||||
|
||||
if (!AuthorizedInstall(bundlePath, destinationPath, &authorizationCanceled)) {
|
||||
if (authorizationCanceled) {
|
||||
NSLog(@"INFO -- Not moving because user canceled authorization");
|
||||
MoveInProgress = NO;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
NSLog(@"ERROR -- Could not copy myself to /Applications with authorization");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If a copy already exists in the Applications folder, put it in the Trash
|
||||
if ([fm fileExistsAtPath:destinationPath]) {
|
||||
// But first, make sure that it's not running
|
||||
if (IsApplicationAtPathRunning(destinationPath)) {
|
||||
// Give the running app focus and terminate myself
|
||||
NSLog(@"INFO -- Switching to an already running version");
|
||||
[[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:[NSArray arrayWithObject:destinationPath]] waitUntilExit];
|
||||
MoveInProgress = NO;
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
if (!Trash([applicationsDirectory stringByAppendingPathComponent:bundleName]))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CopyBundle(bundlePath, destinationPath)) {
|
||||
NSLog(@"ERROR -- Could not copy myself to %@", destinationPath);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// Trash the original app. It's okay if this fails.
|
||||
// NOTE: This final delete does not work if the source bundle is in a network mounted volume.
|
||||
// Calling rm or file manager's delete method doesn't work either. It's unlikely to happen
|
||||
// but it'd be great if someone could fix this.
|
||||
if (!isNestedApplication && diskImageDevice == nil && !DeleteOrTrash(bundlePath)) {
|
||||
NSLog(@"WARNING -- Could not delete application after moving it to Applications folder");
|
||||
}
|
||||
|
||||
// Relaunch.
|
||||
Relaunch(destinationPath);
|
||||
|
||||
// Launched from within a disk image? -- unmount (if no files are open after 5 seconds,
|
||||
// otherwise leave it mounted).
|
||||
if (diskImageDevice && !isNestedApplication) {
|
||||
NSString *script = [NSString stringWithFormat:@"(/bin/sleep 5 && /usr/bin/hdiutil detach %@) &", ShellQuotedString(diskImageDevice)];
|
||||
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||
}
|
||||
|
||||
MoveInProgress = NO;
|
||||
exit(0);
|
||||
}
|
||||
// Save the alert suppress preference if checked
|
||||
else if ([[alert suppressionButton] state] == NSOnState) {
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:AlertSuppressKey];
|
||||
}
|
||||
|
||||
MoveInProgress = NO;
|
||||
return;
|
||||
|
||||
fail:
|
||||
{
|
||||
// Show failure message
|
||||
alert = [[[NSAlert alloc] init] autorelease];
|
||||
[alert setMessageText:kStrMoveApplicationCouldNotMove];
|
||||
[alert runModal];
|
||||
MoveInProgress = NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL PFMoveIsInProgress() {
|
||||
return MoveInProgress;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Helper Functions
|
||||
|
||||
static NSString *PreferredInstallLocation(BOOL *isUserDirectory) {
|
||||
// Return the preferred install location.
|
||||
// Assume that if the user has a ~/Applications folder, they'd prefer their
|
||||
// applications to go there.
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
/*
|
||||
NSArray *userApplicationsDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
|
||||
|
||||
if ([userApplicationsDirs count] > 0) {
|
||||
NSString *userApplicationsDir = [userApplicationsDirs objectAtIndex:0];
|
||||
BOOL isDirectory;
|
||||
|
||||
if ([fm fileExistsAtPath:userApplicationsDir isDirectory:&isDirectory] && isDirectory) {
|
||||
// User Applications directory exists. Get the directory contents.
|
||||
NSArray *contents = [fm contentsOfDirectoryAtPath:userApplicationsDir error:NULL];
|
||||
|
||||
// Check if there is at least one ".app" inside the directory.
|
||||
for (NSString *contentsPath in contents) {
|
||||
if ([[contentsPath pathExtension] isEqualToString:@"app"]) {
|
||||
if (isUserDirectory) *isUserDirectory = YES;
|
||||
return [userApplicationsDir stringByResolvingSymlinksInPath];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// No user Applications directory in use. Return the machine local Applications directory
|
||||
if (isUserDirectory) *isUserDirectory = NO;
|
||||
|
||||
return [[NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSLocalDomainMask, YES) lastObject] stringByResolvingSymlinksInPath];
|
||||
}
|
||||
|
||||
static BOOL IsInApplicationsFolder(NSString *path) {
|
||||
// Check all the normal Application directories
|
||||
NSArray *applicationDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSAllDomainsMask, YES);
|
||||
for (NSString *appDir in applicationDirs) {
|
||||
if ([path hasPrefix:appDir]) return YES;
|
||||
}
|
||||
|
||||
// Also, handle the case that the user has some other Application directory (perhaps on a separate data partition).
|
||||
if ([[path pathComponents] containsObject:@"Applications"]) return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL IsInDownloadsFolder(NSString *path) {
|
||||
NSArray *downloadDirs = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSAllDomainsMask, YES);
|
||||
for (NSString *downloadsDirPath in downloadDirs) {
|
||||
if ([path hasPrefix:downloadsDirPath]) return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL IsApplicationAtPathRunning(NSString *bundlePath) {
|
||||
bundlePath = [bundlePath stringByStandardizingPath];
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||
// Use the new API on 10.6 or higher to determine if the app is already running
|
||||
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||
for (NSRunningApplication *runningApplication in [[NSWorkspace sharedWorkspace] runningApplications]) {
|
||||
NSString *runningAppBundlePath = [[[runningApplication bundleURL] path] stringByStandardizingPath];
|
||||
if ([runningAppBundlePath isEqualToString:bundlePath]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
#endif
|
||||
// Use the shell to determine if the app is already running on systems 10.5 or lower
|
||||
NSString *script = [NSString stringWithFormat:@"/bin/ps ax -o comm | /usr/bin/grep %@/ | /usr/bin/grep -v grep >/dev/null", ShellQuotedString(bundlePath)];
|
||||
NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||
[task waitUntilExit];
|
||||
|
||||
// If the task terminated with status 0, it means that the final grep produced 1 or more lines of output.
|
||||
// Which means that the app is already running
|
||||
return [task terminationStatus] == 0;
|
||||
}
|
||||
|
||||
static BOOL IsApplicationAtPathNested(NSString *path) {
|
||||
NSString *containingPath = [path stringByDeletingLastPathComponent];
|
||||
|
||||
NSArray *components = [containingPath pathComponents];
|
||||
for (NSString *component in components) {
|
||||
if ([[component pathExtension] isEqualToString:@"app"]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static NSString *ContainingDiskImageDevice(NSString *path) {
|
||||
NSString *containingPath = [path stringByDeletingLastPathComponent];
|
||||
|
||||
struct statfs fs;
|
||||
if (statfs([containingPath fileSystemRepresentation], &fs) || (fs.f_flags & MNT_ROOTFS))
|
||||
return nil;
|
||||
|
||||
NSString *device = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:fs.f_mntfromname length:strlen(fs.f_mntfromname)];
|
||||
|
||||
NSTask *hdiutil = [[[NSTask alloc] init] autorelease];
|
||||
[hdiutil setLaunchPath:@"/usr/bin/hdiutil"];
|
||||
[hdiutil setArguments:[NSArray arrayWithObjects:@"info", @"-plist", nil]];
|
||||
[hdiutil setStandardOutput:[NSPipe pipe]];
|
||||
[hdiutil launch];
|
||||
[hdiutil waitUntilExit];
|
||||
|
||||
NSData *data = [[[hdiutil standardOutput] fileHandleForReading] readDataToEndOfFile];
|
||||
NSDictionary *info = nil;
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||
info = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:NULL];
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
|
||||
info = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:NULL];
|
||||
#endif
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5
|
||||
}
|
||||
#endif
|
||||
|
||||
if (![info isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
NSArray *images = (NSArray *)[info objectForKey:@"images"];
|
||||
if (![images isKindOfClass:[NSArray class]]) return nil;
|
||||
|
||||
for (NSDictionary *image in images) {
|
||||
if (![image isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
id systemEntities = [image objectForKey:@"system-entities"];
|
||||
if (![systemEntities isKindOfClass:[NSArray class]]) return nil;
|
||||
|
||||
for (NSDictionary *systemEntity in systemEntities) {
|
||||
if (![systemEntity isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
NSString *devEntry = [systemEntity objectForKey:@"dev-entry"];
|
||||
if (![devEntry isKindOfClass:[NSString class]]) return nil;
|
||||
|
||||
if ([devEntry isEqualToString:device])
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static BOOL Trash(NSString *path) {
|
||||
BOOL result = NO;
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
|
||||
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) {
|
||||
result = [[NSFileManager defaultManager] trashItemAtURL:[NSURL fileURLWithPath:path] resultingItemURL:NULL error:NULL];
|
||||
}
|
||||
#endif
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
|
||||
if (!result) {
|
||||
result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
|
||||
source:[path stringByDeletingLastPathComponent]
|
||||
destination:@""
|
||||
files:[NSArray arrayWithObject:[path lastPathComponent]]
|
||||
tag:NULL];
|
||||
}
|
||||
#endif
|
||||
|
||||
// As a last resort try trashing with AppleScript.
|
||||
// This allows us to trash the app in macOS Sierra even when the app is running inside
|
||||
// an app translocation image.
|
||||
if (!result) {
|
||||
NSAppleScript *appleScript = [[[NSAppleScript alloc] initWithSource:
|
||||
[NSString stringWithFormat:@"\
|
||||
set theFile to POSIX file \"%@\" \n\
|
||||
tell application \"Finder\" \n\
|
||||
move theFile to trash \n\
|
||||
end tell", path]] autorelease];
|
||||
NSDictionary *errorDict = nil;
|
||||
NSAppleEventDescriptor *scriptResult = [appleScript executeAndReturnError:&errorDict];
|
||||
if (scriptResult == nil) {
|
||||
NSLog(@"Trash AppleScript error: %@", errorDict);
|
||||
}
|
||||
result = (scriptResult != nil);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
NSLog(@"ERROR -- Could not trash '%@'", path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL DeleteOrTrash(NSString *path) {
|
||||
NSError *error;
|
||||
|
||||
if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
// Don't log warning if on Sierra and running inside App Translocation path
|
||||
if ([path rangeOfString:@"/AppTranslocation/"].location == NSNotFound)
|
||||
NSLog(@"WARNING -- Could not delete '%@': %@", path, [error localizedDescription]);
|
||||
|
||||
return Trash(path);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled) {
|
||||
if (canceled) *canceled = NO;
|
||||
|
||||
// Make sure that the destination path is an app bundle. We're essentially running 'sudo rm -rf'
|
||||
// so we really don't want to fuck this up.
|
||||
if (![[dstPath pathExtension] isEqualToString:@"app"]) return NO;
|
||||
|
||||
// Do some more checks
|
||||
if ([[dstPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
|
||||
if ([[srcPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
|
||||
|
||||
int pid, status;
|
||||
AuthorizationRef myAuthorizationRef;
|
||||
|
||||
// Get the authorization
|
||||
OSStatus err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &myAuthorizationRef);
|
||||
if (err != errAuthorizationSuccess) return NO;
|
||||
|
||||
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
|
||||
AuthorizationRights myRights = {1, &myItems};
|
||||
AuthorizationFlags myFlags = (AuthorizationFlags)(kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize);
|
||||
|
||||
err = AuthorizationCopyRights(myAuthorizationRef, &myRights, NULL, myFlags, NULL);
|
||||
if (err != errAuthorizationSuccess) {
|
||||
if (err == errAuthorizationCanceled && canceled)
|
||||
*canceled = YES;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
static OSStatus (*security_AuthorizationExecuteWithPrivileges)(AuthorizationRef authorization, const char *pathToTool,
|
||||
AuthorizationFlags options, char * const *arguments,
|
||||
FILE **communicationsPipe) = NULL;
|
||||
if (!security_AuthorizationExecuteWithPrivileges) {
|
||||
// On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want to still use it since there's no
|
||||
// good alternative (without requiring code signing). We'll look up the function through dyld and fail
|
||||
// if it is no longer accessible. If Apple removes the function entirely this will fail gracefully. If
|
||||
// they keep the function and throw some sort of exception, this won't fail gracefully, but that's a
|
||||
// risk we'll have to take for now.
|
||||
security_AuthorizationExecuteWithPrivileges = (OSStatus (*)(AuthorizationRef, const char*,
|
||||
AuthorizationFlags, char* const*,
|
||||
FILE **)) dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges");
|
||||
}
|
||||
if (!security_AuthorizationExecuteWithPrivileges) goto fail;
|
||||
|
||||
// Delete the destination
|
||||
{
|
||||
char *args[] = {"-rf", (char *)[dstPath fileSystemRepresentation], NULL};
|
||||
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/rm", kAuthorizationFlagDefaults, args, NULL);
|
||||
if (err != errAuthorizationSuccess) goto fail;
|
||||
|
||||
// Wait until it's done
|
||||
pid = wait(&status);
|
||||
if (pid == -1 || !WIFEXITED(status)) goto fail; // We don't care about exit status as the destination most likely does not exist
|
||||
}
|
||||
|
||||
// Copy
|
||||
{
|
||||
char *args[] = {"-pR", (char *)[srcPath fileSystemRepresentation], (char *)[dstPath fileSystemRepresentation], NULL};
|
||||
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/cp", kAuthorizationFlagDefaults, args, NULL);
|
||||
if (err != errAuthorizationSuccess) goto fail;
|
||||
|
||||
// Wait until it's done
|
||||
pid = wait(&status);
|
||||
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) goto fail;
|
||||
}
|
||||
|
||||
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
|
||||
return YES;
|
||||
|
||||
fail:
|
||||
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
if ([fm copyItemAtPath:srcPath toPath:dstPath error:&error]) {
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
NSLog(@"ERROR -- Could not copy '%@' to '%@' (%@)", srcPath, dstPath, error);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
static NSString *ShellQuotedString(NSString *string) {
|
||||
return [NSString stringWithFormat:@"'%@'", [string stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]];
|
||||
}
|
||||
|
||||
static void Relaunch(NSString *destinationPath) {
|
||||
// The shell script waits until the original app process terminates.
|
||||
// This is done so that the relaunched app opens as the front-most app.
|
||||
int pid = [[NSProcessInfo processInfo] processIdentifier];
|
||||
|
||||
// Command run just before running open /final/path
|
||||
NSString *preOpenCmd = @"";
|
||||
|
||||
NSString *quotedDestinationPath = ShellQuotedString(destinationPath);
|
||||
|
||||
// OS X >=10.5:
|
||||
// Before we launch the new app, clear xattr:com.apple.quarantine to avoid
|
||||
// duplicate "scary file from the internet" dialog.
|
||||
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
|
||||
// Add the -r flag on 10.6
|
||||
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d -r com.apple.quarantine %@", quotedDestinationPath];
|
||||
}
|
||||
else {
|
||||
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d com.apple.quarantine %@", quotedDestinationPath];
|
||||
}
|
||||
|
||||
NSString *script = [NSString stringWithFormat:@"(while /bin/kill -0 %d >&/dev/null; do /bin/sleep 0.1; done; %@; /usr/bin/open %@) &", pid, preOpenCmd, quotedDestinationPath];
|
||||
|
||||
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
# -fobjc-arc: enables ARC
|
||||
# -fmodules: enables modules so you can import with `@import AppKit;`
|
||||
# -mmacosx-version-min=10.6: support older OS X versions, this might increase the binary size
|
||||
|
||||
if [ ! -d "../dist" ]; then mkdir ../dist; fi
|
||||
|
||||
clang PFMoveApplication.m -fno-objc-arc -fmodules -mmacosx-version-min=10.6 -c -o PFMoveApplication.o
|
||||
clang run-with-mono.m Launcher.m PFMoveApplication.o -fobjc-arc -fmodules -mmacosx-version-min=10.6 -o ../dist/Launcher
|
||||
rm PFMoveApplication.o
|
||||
|
||||
if [ "$1" == "install" ] && [ "$2" != "" ]; then
|
||||
echo "Installing to $2"
|
||||
cp ../dist/Launcher $2
|
||||
chmod +x $2
|
||||
fi
|
@ -1,11 +0,0 @@
|
||||
@import Foundation;
|
||||
@import AppKit;
|
||||
|
||||
@interface RunWithMono : NSObject {
|
||||
}
|
||||
|
||||
+ (void) openDownloadLink:(NSButton*)button;
|
||||
+ (bool) showDownloadMonoDialog:(NSString *)appName major:(int)major minor:(int)minor;
|
||||
+ (int) runAssemblyWithMono:(NSString *)appName procnamesuffix:(NSString *)procnamesuffix assembly:(NSString *)assembly major:(int) major minor:(int) minor;
|
||||
|
||||
@end
|
@ -1,258 +0,0 @@
|
||||
#import "run-with-mono.h"
|
||||
|
||||
@import Foundation;
|
||||
@import AppKit;
|
||||
|
||||
NSString * const VERSION_TITLE = @"Cannot launch %@";
|
||||
NSString * const VERSION_MSG = @"%@ requires the Mono Framework version %d.%d or later.";
|
||||
NSString * const DOWNLOAD_URL = @"http://www.mono-project.com/download/stable/#download-mac";
|
||||
|
||||
// Helper method to see if the user has requested debug output
|
||||
bool D() {
|
||||
NSString* v = [[[NSProcessInfo processInfo]environment]objectForKey:@"DEBUG"];
|
||||
if (v == nil || v.length == 0 || [v isEqual:@"0"] || [v isEqual:@"false"] || [v isEqual:@"f"])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wrapper method to invoke commandline operations and return the string output
|
||||
NSString *runCommand(NSString *program, NSArray<NSString *> *arguments) {
|
||||
NSPipe *pipe = [NSPipe pipe];
|
||||
NSFileHandle *file = pipe.fileHandleForReading;
|
||||
|
||||
NSTask *task = [[NSTask alloc] init];
|
||||
task.launchPath = program;
|
||||
task.arguments = arguments;
|
||||
task.standardOutput = pipe;
|
||||
|
||||
[task launch];
|
||||
|
||||
NSData *data = [file readDataToEndOfFile];
|
||||
[file closeFile];
|
||||
[task waitUntilExit];
|
||||
|
||||
NSString *cmdOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
|
||||
if (cmdOutput == nil || cmdOutput.length == 0)
|
||||
return nil;
|
||||
|
||||
return [cmdOutput stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
}
|
||||
|
||||
// Checks if the Mono version is greater than or equal to the desired version
|
||||
bool isValidMono(NSString *mono, int major, int minor) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if (mono == nil)
|
||||
return false;
|
||||
|
||||
if (![fileManager fileExistsAtPath:mono] || ![fileManager isExecutableFileAtPath:mono])
|
||||
return false;
|
||||
|
||||
NSString *versionInfo = runCommand(mono, @[@"--version"]);
|
||||
|
||||
NSRange rg = [versionInfo rangeOfString:@"Mono JIT compiler version \\d+\\.\\d+" options:NSRegularExpressionSearch];
|
||||
if (rg.location != NSNotFound) {
|
||||
versionInfo = [versionInfo substringWithRange:rg];
|
||||
if (D()) NSLog(@"Matched version: %@", versionInfo);
|
||||
rg = [versionInfo rangeOfString:@"\\d+\\.\\d+" options:NSRegularExpressionSearch];
|
||||
if (rg.location != NSNotFound) {
|
||||
versionInfo = [versionInfo substringWithRange:rg];
|
||||
if (D()) NSLog(@"Matched version: %@", versionInfo);
|
||||
|
||||
NSArray<NSString *> *versionComponents = [versionInfo componentsSeparatedByString:@"."];
|
||||
if ([versionComponents[0] intValue] < major)
|
||||
return false;
|
||||
if ([versionComponents[0] intValue] == major && [versionComponents[1] intValue] < minor)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to locate a mono with a valid version
|
||||
NSString *findMono(int major, int minor) {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
NSString *currentMono = runCommand(@"/usr/bin/which", @[@"mono"]);
|
||||
if (D()) NSLog(@"which mono: %@", currentMono);
|
||||
|
||||
if (isValidMono(currentMono, major, minor)) {
|
||||
if (D()) NSLog(@"Found mono with: %@", currentMono);
|
||||
return currentMono;
|
||||
}
|
||||
|
||||
NSArray *probepaths = @[@"/usr/local/bin/mono", @"/Library/Frameworks/Mono.framework/Versions/Current/bin/mono", @"/opt/local/bin/mono"];
|
||||
for(NSString* probepath in probepaths) {
|
||||
if (D()) NSLog(@"Trying mono with: %@", probepath);
|
||||
if (isValidMono(probepath, major, minor)) {
|
||||
if (D()) NSLog(@"Found mono with: %@", probepath);
|
||||
return probepath;
|
||||
}
|
||||
}
|
||||
|
||||
if (D()) NSLog(@"Failed to find Mono, returning: %@", nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Check Bundle for quarantine
|
||||
void checkBundle() {
|
||||
|
||||
NSString * const bundlePath = [[NSBundle mainBundle] bundlePath];
|
||||
NSString * const attributes = runCommand(@"/usr/bin/xattr", @[@"-l", bundlePath]);
|
||||
if (D()) NSLog(@"Attributes: %@", attributes);
|
||||
if ([attributes containsString:@"com.apple.quarantine:"]) {
|
||||
runCommand(@"/usr/bin/xattr", @[@"-dr", @"com.apple.quarantine", bundlePath]);
|
||||
NSLog(@"Removed quarantine attribute from bundle");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@implementation RunWithMono
|
||||
|
||||
+ (void) openDownloadLink:(NSButton*)button {
|
||||
if (D()) NSLog(@"Clicked Download");
|
||||
runCommand(@"/usr/bin/open", @[DOWNLOAD_URL]);
|
||||
}
|
||||
|
||||
// Shows the download dialog, prompting to download Mono
|
||||
+ (bool) showDownloadMonoDialog:(NSString *)appName major:(int)major minor:(int)minor {
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
|
||||
[alert setInformativeText:[NSString stringWithFormat:VERSION_MSG, appName, major, minor]];
|
||||
[alert setMessageText:[NSString stringWithFormat:VERSION_TITLE, appName]];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
[alert addButtonWithTitle:@"Retry"];
|
||||
[alert addButtonWithTitle:@"Download"];
|
||||
|
||||
NSButton *downloadButton = [[alert buttons] objectAtIndex:2];
|
||||
|
||||
[downloadButton setTarget:self];
|
||||
[downloadButton setAction:@selector(openDownloadLink:)];
|
||||
|
||||
NSModalResponse btn = [alert runModal];
|
||||
if (btn == NSAlertFirstButtonReturn) {
|
||||
if (D()) NSLog(@"Clicked Cancel");
|
||||
return true;
|
||||
}
|
||||
else if (btn == NSAlertSecondButtonReturn) {
|
||||
if (D()) NSLog(@"Clicked Retry");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Top-level method, finds Mono with an appropriate version and launches the assembly
|
||||
+ (int) runAssemblyWithMono: (NSString *)appName procnamesuffix:(NSString *)procnamesuffix assembly:(NSString *)assembly major:(int) major minor:(int) minor {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
NSString *assemblyPath;
|
||||
bool found = false;
|
||||
|
||||
NSString *localPath = NSProcessInfo.processInfo.arguments[0].stringByDeletingLastPathComponent;
|
||||
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
|
||||
NSArray *paths = @[
|
||||
localPath,
|
||||
[NSString pathWithComponents:@[localPath, @"bin"]],
|
||||
resourcePath,
|
||||
[NSString pathWithComponents:@[resourcePath, @"bin"]]
|
||||
];
|
||||
for (NSString* entryFolder in paths) {
|
||||
if (D()) NSLog(@"Checking folder: %@", entryFolder);
|
||||
|
||||
assemblyPath = [NSString pathWithComponents:@[entryFolder, assembly]];
|
||||
|
||||
if ([fileManager fileExistsAtPath:assemblyPath]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
NSLog(@"Assembly file not found");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (D()) NSLog(@"assemblyPath: %@", assemblyPath);
|
||||
|
||||
checkBundle();
|
||||
|
||||
NSString *currentMono = findMono(major, minor);
|
||||
|
||||
while (currentMono == nil) {
|
||||
NSLog(@"No valid mono found!");
|
||||
bool close = [self showDownloadMonoDialog:appName major:major minor:minor];
|
||||
if (close)
|
||||
return 1;
|
||||
currentMono = findMono(major, minor);
|
||||
}
|
||||
|
||||
// Setup dylib fallback loading
|
||||
NSMutableArray * dylibPath = [NSMutableArray arrayWithObject:assemblyPath.stringByDeletingLastPathComponent];
|
||||
|
||||
// Update the PATH to use the specified mono version
|
||||
if ([currentMono hasPrefix:@"/"])
|
||||
{
|
||||
NSString * curMonoBinDir = currentMono.stringByDeletingLastPathComponent;
|
||||
NSString * curMonoDir = curMonoBinDir.stringByDeletingLastPathComponent;
|
||||
NSString * curMonoLibDir = [NSString pathWithComponents:@[curMonoDir, @"lib"]];
|
||||
|
||||
NSString * curEnvPath = [NSString stringWithUTF8String:getenv("PATH")];
|
||||
NSString * newEnvPath = [NSString stringWithFormat:@"%@:%@", curMonoBinDir, curEnvPath];
|
||||
setenv("PATH", newEnvPath.UTF8String, 1);
|
||||
|
||||
[dylibPath addObject:curMonoLibDir];
|
||||
|
||||
NSLog(@"Added %@ to PATH", curMonoBinDir);
|
||||
}
|
||||
|
||||
// Setup libsqlite?
|
||||
/* if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
|
||||
fi
|
||||
*/
|
||||
|
||||
[dylibPath addObjectsFromArray:@[@"$HOME/lib", @"/usr/local/lib", @"/lib", @"/usr/lib"]];
|
||||
|
||||
setenv("DYLD_FALLBACK_LIBRARY_PATH", [dylibPath componentsJoinedByString:@":"].UTF8String, 1);
|
||||
|
||||
if (D()) NSLog(@"Running %@ --debug %@", currentMono, assemblyPath);
|
||||
|
||||
// Copy commandline arguments
|
||||
NSMutableArray* arguments = [[NSMutableArray alloc] init];
|
||||
// Disabled suffix for now coz it's confusing and not preserved on in-app restart
|
||||
[arguments addObject:currentMono];
|
||||
//[arguments addObject:[currentMono stringByAppendingString:procnamesuffix]];
|
||||
[arguments addObject:@"--debug"];
|
||||
[arguments addObjectsFromArray:[[NSProcessInfo processInfo] arguments]];
|
||||
|
||||
// replace the executable-path with the assembly path
|
||||
[arguments replaceObjectAtIndex:2 withObject:assemblyPath];
|
||||
|
||||
// Try switch to mono using execv
|
||||
char * cPath = strdup([currentMono UTF8String]);
|
||||
char ** cArgs;
|
||||
char ** pArgNext = cArgs = malloc(sizeof(*cArgs) * ([arguments count] + 1));
|
||||
for (NSString *s in arguments) {
|
||||
*pArgNext++ = strdup([s UTF8String]);
|
||||
}
|
||||
*pArgNext = NULL;
|
||||
int ret = execv(cPath, cArgs);
|
||||
if (ret != 0)
|
||||
NSLog(@"Failed execv with errno @d", errno);
|
||||
// execv failed, cleanup
|
||||
pArgNext = cArgs;
|
||||
for (NSString *s in arguments) {
|
||||
free(*pArgNext++);
|
||||
}
|
||||
free(cArgs);
|
||||
free(cPath);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@end
|
@ -1,62 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#get the bundle's MacOS directory full path
|
||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
|
||||
#change these values to match your app
|
||||
EXE_PATH="$DIR/Sonarr.exe"
|
||||
APPNAME="Sonarr"
|
||||
|
||||
#set up environment
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
# Macports and mono-supplied installer path
|
||||
export PATH="/opt/local/bin:$PATH"
|
||||
elif [[ -x '/usr/local/bin/mono' ]]; then
|
||||
# Homebrew-supplied path to mono
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
|
||||
|
||||
if [ -e /Library/Frameworks/Mono.framework ]; then
|
||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
||||
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$MONO_FRAMEWORK_PATH/lib"
|
||||
fi
|
||||
|
||||
if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$HOME/lib:/usr/local/lib:/lib:/usr/lib"
|
||||
|
||||
#mono version check
|
||||
REQUIRED_MAJOR=4
|
||||
REQUIRED_MINOR=6
|
||||
|
||||
VERSION_TITLE="Cannot launch $APPNAME"
|
||||
VERSION_MSG="$APPNAME requires Mono Runtime Environment(MRE) $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
|
||||
DOWNLOAD_URL="http://www.mono-project.com/download/#download-mac"
|
||||
|
||||
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
|
||||
# if [[ -o DEBUG ]]; then osascript -e "display dialog \"MONO_VERSION: $MONO_VERSION\""; fi
|
||||
|
||||
|
||||
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
|
||||
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
|
||||
if [ -z "$MONO_VERSION" ] \
|
||||
|| [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
|
||||
|| [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ]
|
||||
then
|
||||
osascript \
|
||||
-e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
|
||||
-e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
|
||||
echo "$VERSION_TITLE"
|
||||
echo "$VERSION_MSG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MONO_EXEC="exec mono --debug"
|
||||
|
||||
#run app using mono
|
||||
$MONO_EXEC "$EXE_PATH"
|
@ -7,8 +7,9 @@
|
||||
#define ForumsURL "https://forums.sonarr.tv/"
|
||||
#define AppExeName "Sonarr.exe"
|
||||
#define BuildNumber "3.0"
|
||||
#define BuildNumber GetEnv('BUILD_NUMBER')
|
||||
#define BranchName GetEnv('BRANCH')
|
||||
#define Framework GetEnv('FRAMEWORK')
|
||||
#define Runtime GetEnv('RUNTIME')
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
@ -16,7 +17,7 @@
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{56C1065D-3523-4025-B76D-6F73F67F7F71}
|
||||
AppName={#AppName}
|
||||
AppVersion=3.0
|
||||
AppVersion={#BuildNumber}
|
||||
AppPublisher={#AppPublisher}
|
||||
AppPublisherURL={#AppURL}
|
||||
AppSupportURL={#ForumsURL}
|
||||
@ -26,7 +27,7 @@ DefaultDirName={commonappdata}\Sonarr\bin
|
||||
DisableDirPage=yes
|
||||
DefaultGroupName={#AppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputBaseFilename=Sonarr.{#BranchName}.{#BuildNumber}.windows
|
||||
OutputBaseFilename=Sonarr.{#BranchName}.{#BuildNumber}.windows.{#Framework}
|
||||
SolidCompression=yes
|
||||
AppCopyright=Creative Commons 3.0 License
|
||||
AllowUNCPath=False
|
||||
@ -49,8 +50,8 @@ Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts
|
||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
|
||||
[Files]
|
||||
Source: "..\..\..\_output_windows\Sonarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\..\_output_windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Sonarr\Sonarr.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Sonarr\*"; Excludes: "Sonarr.Update"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
@ -80,26 +81,3 @@ begin
|
||||
Exec('net', 'stop nzbdrone', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('sc', 'delete nzbdrone', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
end;
|
||||
|
||||
function Framework472IsNotInstalled(): Boolean;
|
||||
var
|
||||
bSuccess: Boolean;
|
||||
regVersion: Cardinal;
|
||||
begin
|
||||
Result := True;
|
||||
bSuccess := RegQueryDWordValue(HKLM, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', regVersion);
|
||||
if (True = bSuccess) and (regVersion >= 461808) then begin
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
if Framework472IsNotInstalled() then begin
|
||||
MsgBox('Sonarr requires Microsoft .NET Framework 4.7.2 or higher.'#13#13
|
||||
'Please use Windows Update to install this version'#13
|
||||
'or download it from https://dotnet.microsoft.com/download/dotnet-framework.', mbInformation, MB_OK);
|
||||
result := false;
|
||||
end else
|
||||
result := true;
|
||||
end;
|
||||
|
@ -18,7 +18,8 @@ class About extends Component {
|
||||
version,
|
||||
packageVersion,
|
||||
packageAuthor,
|
||||
isMonoRuntime,
|
||||
isNetCore,
|
||||
isDocker,
|
||||
runtimeVersion,
|
||||
appData,
|
||||
startupPath,
|
||||
@ -45,14 +46,18 @@ class About extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
isMonoRuntime ?
|
||||
isNetCore &&
|
||||
<DescriptionListItem
|
||||
title="Mono Version"
|
||||
data={runtimeVersion}
|
||||
/> :
|
||||
title=".Net Version"
|
||||
data={`Yes (${runtimeVersion})`}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
isDocker &&
|
||||
<DescriptionListItem
|
||||
title=".net Version"
|
||||
data={runtimeVersion}
|
||||
title="Docker"
|
||||
data={'Yes'}
|
||||
/>
|
||||
}
|
||||
|
||||
@ -92,8 +97,9 @@ About.propTypes = {
|
||||
version: PropTypes.string.isRequired,
|
||||
packageVersion: PropTypes.string,
|
||||
packageAuthor: PropTypes.string,
|
||||
isMonoRuntime: PropTypes.bool.isRequired,
|
||||
isNetCore: PropTypes.bool.isRequired,
|
||||
runtimeVersion: PropTypes.string.isRequired,
|
||||
isDocker: PropTypes.bool.isRequired,
|
||||
appData: PropTypes.string.isRequired,
|
||||
startupPath: PropTypes.string.isRequired,
|
||||
mode: PropTypes.string.isRequired,
|
||||
|
@ -29,7 +29,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.3",
|
||||
"@fortawesome/react-fontawesome": "0.1.14",
|
||||
"@microsoft/signalr": "5.0.5",
|
||||
"@microsoft/signalr": "6.0.3",
|
||||
"@sentry/browser": "6.3.1",
|
||||
"@sentry/integrations": "6.3.1",
|
||||
"classnames": "2.3.1",
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Dictionary>
|
||||
<Words>
|
||||
<Recognized>
|
||||
<Word>Ack</Word>
|
||||
<Word>Minifier</Word>
|
||||
<Word>Jsonp</Word>
|
||||
<Word>Linktionary</Word>
|
||||
<Word>Scaleout</Word>
|
||||
<Word>Redis</Word>
|
||||
<Word>Owin</Word>
|
||||
<Word>Stringify</Word>
|
||||
<Word>Unminify</Word>
|
||||
<Word>Unminified</Word>
|
||||
<Word>Stateful</Word>
|
||||
<Word>SignalR</Word>
|
||||
<Word>Hubservable</Word>
|
||||
<Word>Sse</Word>
|
||||
<Word>GitHub</Word>
|
||||
</Recognized>
|
||||
</Words>
|
||||
</Dictionary>
|
@ -1,20 +0,0 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyCompany("Microsoft Open Technologies, Inc.")]
|
||||
[assembly: AssemblyCopyright("© Microsoft Open Technologies, Inc. All rights reserved.")]
|
||||
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: CLSCompliant(false)]
|
||||
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
@ -1,5 +0,0 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("10.0.0.*")]
|
@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
|
||||
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
//
|
||||
// To add a suppression to this file, right-click the message in the
|
||||
// Code Analysis results, point to "Suppress Message", and click
|
||||
// "In Suppression File".
|
||||
// You do not need to add suppressions to this file manually.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Strong naming is done on the CI.")]
|
||||
[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "param", Scope = "resource", Target = "Microsoft.AspNet.SignalR.Resources.resources")]
|
||||
[assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "We use semver")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.AspNet.SignalR.Messaging.ScaleoutTaskQueue.#.cctor()", Justification = "The task is cached")]
|
@ -1,227 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="New Rule Set" Description=" " ToolsVersion="11.0">
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
|
||||
<Rule Id="CA1000" Action="Error" />
|
||||
<Rule Id="CA1001" Action="Error" />
|
||||
<Rule Id="CA1002" Action="Error" />
|
||||
<Rule Id="CA1003" Action="Error" />
|
||||
<Rule Id="CA1004" Action="Error" />
|
||||
<Rule Id="CA1005" Action="Error" />
|
||||
<Rule Id="CA1007" Action="Error" />
|
||||
<Rule Id="CA1008" Action="Error" />
|
||||
<Rule Id="CA1010" Action="Error" />
|
||||
<Rule Id="CA1011" Action="Error" />
|
||||
<Rule Id="CA1012" Action="Error" />
|
||||
<Rule Id="CA1013" Action="Error" />
|
||||
<Rule Id="CA1014" Action="Error" />
|
||||
<Rule Id="CA1016" Action="Error" />
|
||||
<Rule Id="CA1017" Action="Error" />
|
||||
<Rule Id="CA1018" Action="Error" />
|
||||
<Rule Id="CA1019" Action="Error" />
|
||||
<Rule Id="CA1021" Action="Error" />
|
||||
<Rule Id="CA1023" Action="Error" />
|
||||
<Rule Id="CA1024" Action="Error" />
|
||||
<Rule Id="CA1025" Action="Error" />
|
||||
<Rule Id="CA1026" Action="Error" />
|
||||
<Rule Id="CA1027" Action="Error" />
|
||||
<Rule Id="CA1028" Action="Error" />
|
||||
<Rule Id="CA1030" Action="Error" />
|
||||
<Rule Id="CA1031" Action="Error" />
|
||||
<Rule Id="CA1032" Action="Error" />
|
||||
<Rule Id="CA1034" Action="Error" />
|
||||
<Rule Id="CA1035" Action="Error" />
|
||||
<Rule Id="CA1036" Action="Error" />
|
||||
<Rule Id="CA1038" Action="Error" />
|
||||
<Rule Id="CA1039" Action="Error" />
|
||||
<Rule Id="CA1040" Action="Error" />
|
||||
<Rule Id="CA1041" Action="Error" />
|
||||
<Rule Id="CA1043" Action="Error" />
|
||||
<Rule Id="CA1044" Action="Error" />
|
||||
<Rule Id="CA1045" Action="Error" />
|
||||
<Rule Id="CA1046" Action="Error" />
|
||||
<Rule Id="CA1047" Action="Error" />
|
||||
<Rule Id="CA1048" Action="Error" />
|
||||
<Rule Id="CA1049" Action="Error" />
|
||||
<Rule Id="CA1050" Action="Error" />
|
||||
<Rule Id="CA1051" Action="Error" />
|
||||
<Rule Id="CA1052" Action="Error" />
|
||||
<Rule Id="CA1053" Action="Error" />
|
||||
<Rule Id="CA1057" Action="Error" />
|
||||
<Rule Id="CA1058" Action="Error" />
|
||||
<Rule Id="CA1059" Action="Error" />
|
||||
<Rule Id="CA1060" Action="Error" />
|
||||
<Rule Id="CA1061" Action="Error" />
|
||||
<Rule Id="CA1062" Action="Error" />
|
||||
<Rule Id="CA1063" Action="Error" />
|
||||
<Rule Id="CA1064" Action="Error" />
|
||||
<Rule Id="CA1065" Action="Error" />
|
||||
<Rule Id="CA1300" Action="Error" />
|
||||
<Rule Id="CA1301" Action="Error" />
|
||||
<Rule Id="CA1302" Action="Error" />
|
||||
<Rule Id="CA1303" Action="Error" />
|
||||
<Rule Id="CA1304" Action="Error" />
|
||||
<Rule Id="CA1305" Action="Error" />
|
||||
<Rule Id="CA1306" Action="Error" />
|
||||
<Rule Id="CA1307" Action="Error" />
|
||||
<Rule Id="CA1308" Action="Error" />
|
||||
<Rule Id="CA1309" Action="Error" />
|
||||
<Rule Id="CA1400" Action="Error" />
|
||||
<Rule Id="CA1401" Action="Error" />
|
||||
<Rule Id="CA1402" Action="Error" />
|
||||
<Rule Id="CA1403" Action="Error" />
|
||||
<Rule Id="CA1404" Action="Error" />
|
||||
<Rule Id="CA1405" Action="Error" />
|
||||
<Rule Id="CA1406" Action="Error" />
|
||||
<Rule Id="CA1407" Action="Error" />
|
||||
<Rule Id="CA1408" Action="Error" />
|
||||
<Rule Id="CA1409" Action="Error" />
|
||||
<Rule Id="CA1410" Action="Error" />
|
||||
<Rule Id="CA1411" Action="Error" />
|
||||
<Rule Id="CA1412" Action="Error" />
|
||||
<Rule Id="CA1413" Action="Error" />
|
||||
<Rule Id="CA1414" Action="Error" />
|
||||
<Rule Id="CA1415" Action="Error" />
|
||||
<Rule Id="CA1500" Action="Error" />
|
||||
<Rule Id="CA1501" Action="Error" />
|
||||
<Rule Id="CA1502" Action="Error" />
|
||||
<Rule Id="CA1504" Action="Error" />
|
||||
<Rule Id="CA1505" Action="Error" />
|
||||
<Rule Id="CA1506" Action="Error" />
|
||||
<Rule Id="CA1600" Action="Error" />
|
||||
<Rule Id="CA1601" Action="Error" />
|
||||
<Rule Id="CA1700" Action="Error" />
|
||||
<Rule Id="CA1701" Action="Error" />
|
||||
<Rule Id="CA1702" Action="Error" />
|
||||
<Rule Id="CA1703" Action="Error" />
|
||||
<Rule Id="CA1704" Action="Error" />
|
||||
<Rule Id="CA1707" Action="Error" />
|
||||
<Rule Id="CA1708" Action="Error" />
|
||||
<Rule Id="CA1709" Action="Error" />
|
||||
<Rule Id="CA1712" Action="Error" />
|
||||
<Rule Id="CA1713" Action="Error" />
|
||||
<Rule Id="CA1714" Action="Error" />
|
||||
<Rule Id="CA1715" Action="Error" />
|
||||
<Rule Id="CA1716" Action="Error" />
|
||||
<Rule Id="CA1717" Action="Error" />
|
||||
<Rule Id="CA1719" Action="Error" />
|
||||
<Rule Id="CA1720" Action="Error" />
|
||||
<Rule Id="CA1721" Action="Error" />
|
||||
<Rule Id="CA1722" Action="Error" />
|
||||
<Rule Id="CA1724" Action="Error" />
|
||||
<Rule Id="CA1725" Action="Error" />
|
||||
<Rule Id="CA1726" Action="Error" />
|
||||
<Rule Id="CA1800" Action="Error" />
|
||||
<Rule Id="CA1801" Action="Error" />
|
||||
<Rule Id="CA1802" Action="Error" />
|
||||
<Rule Id="CA1804" Action="Error" />
|
||||
<Rule Id="CA1806" Action="Error" />
|
||||
<Rule Id="CA1809" Action="Error" />
|
||||
<Rule Id="CA1810" Action="Error" />
|
||||
<Rule Id="CA1811" Action="Error" />
|
||||
<Rule Id="CA1812" Action="Error" />
|
||||
<Rule Id="CA1813" Action="Error" />
|
||||
<Rule Id="CA1814" Action="Error" />
|
||||
<Rule Id="CA1815" Action="Error" />
|
||||
<Rule Id="CA1819" Action="Error" />
|
||||
<Rule Id="CA1820" Action="Error" />
|
||||
<Rule Id="CA1821" Action="Error" />
|
||||
<Rule Id="CA1822" Action="Error" />
|
||||
<Rule Id="CA1823" Action="Error" />
|
||||
<Rule Id="CA1824" Action="Error" />
|
||||
<Rule Id="CA1900" Action="Error" />
|
||||
<Rule Id="CA1901" Action="Error" />
|
||||
<Rule Id="CA1903" Action="Error" />
|
||||
<Rule Id="CA2000" Action="Error" />
|
||||
<Rule Id="CA2001" Action="Error" />
|
||||
<Rule Id="CA2002" Action="Error" />
|
||||
<Rule Id="CA2003" Action="Error" />
|
||||
<Rule Id="CA2004" Action="Error" />
|
||||
<Rule Id="CA2006" Action="Error" />
|
||||
<Rule Id="CA2100" Action="Error" />
|
||||
<Rule Id="CA2101" Action="Error" />
|
||||
<Rule Id="CA2102" Action="Error" />
|
||||
<Rule Id="CA2103" Action="Error" />
|
||||
<Rule Id="CA2104" Action="Error" />
|
||||
<Rule Id="CA2105" Action="Error" />
|
||||
<Rule Id="CA2106" Action="Error" />
|
||||
<Rule Id="CA2107" Action="Error" />
|
||||
<Rule Id="CA2108" Action="Error" />
|
||||
<Rule Id="CA2109" Action="Error" />
|
||||
<Rule Id="CA2111" Action="Error" />
|
||||
<Rule Id="CA2112" Action="Error" />
|
||||
<Rule Id="CA2114" Action="Error" />
|
||||
<Rule Id="CA2115" Action="Error" />
|
||||
<Rule Id="CA2116" Action="Error" />
|
||||
<Rule Id="CA2117" Action="Error" />
|
||||
<Rule Id="CA2118" Action="Error" />
|
||||
<Rule Id="CA2119" Action="Error" />
|
||||
<Rule Id="CA2120" Action="Error" />
|
||||
<Rule Id="CA2121" Action="Error" />
|
||||
<Rule Id="CA2122" Action="Error" />
|
||||
<Rule Id="CA2123" Action="Error" />
|
||||
<Rule Id="CA2124" Action="Error" />
|
||||
<Rule Id="CA2126" Action="Error" />
|
||||
<Rule Id="CA2130" Action="Error" />
|
||||
<Rule Id="CA2131" Action="Error" />
|
||||
<Rule Id="CA2132" Action="Error" />
|
||||
<Rule Id="CA2133" Action="Error" />
|
||||
<Rule Id="CA2134" Action="Error" />
|
||||
<Rule Id="CA2135" Action="Error" />
|
||||
<Rule Id="CA2136" Action="Error" />
|
||||
<Rule Id="CA2137" Action="Error" />
|
||||
<Rule Id="CA2138" Action="Error" />
|
||||
<Rule Id="CA2139" Action="Error" />
|
||||
<Rule Id="CA2140" Action="Error" />
|
||||
<Rule Id="CA2141" Action="Error" />
|
||||
<Rule Id="CA2142" Action="Error" />
|
||||
<Rule Id="CA2143" Action="Error" />
|
||||
<Rule Id="CA2144" Action="Error" />
|
||||
<Rule Id="CA2145" Action="Error" />
|
||||
<Rule Id="CA2146" Action="Error" />
|
||||
<Rule Id="CA2147" Action="Error" />
|
||||
<Rule Id="CA2149" Action="Error" />
|
||||
<Rule Id="CA2151" Action="Error" />
|
||||
<Rule Id="CA2200" Action="Error" />
|
||||
<Rule Id="CA2201" Action="Error" />
|
||||
<Rule Id="CA2202" Action="Error" />
|
||||
<Rule Id="CA2204" Action="Error" />
|
||||
<Rule Id="CA2205" Action="Error" />
|
||||
<Rule Id="CA2207" Action="Error" />
|
||||
<Rule Id="CA2208" Action="Error" />
|
||||
<Rule Id="CA2210" Action="Error" />
|
||||
<Rule Id="CA2211" Action="Error" />
|
||||
<Rule Id="CA2212" Action="Error" />
|
||||
<Rule Id="CA2213" Action="Error" />
|
||||
<Rule Id="CA2214" Action="Error" />
|
||||
<Rule Id="CA2215" Action="Error" />
|
||||
<Rule Id="CA2216" Action="Error" />
|
||||
<Rule Id="CA2217" Action="Error" />
|
||||
<Rule Id="CA2218" Action="Error" />
|
||||
<Rule Id="CA2219" Action="Error" />
|
||||
<Rule Id="CA2220" Action="Error" />
|
||||
<Rule Id="CA2221" Action="Error" />
|
||||
<Rule Id="CA2222" Action="Error" />
|
||||
<Rule Id="CA2223" Action="Error" />
|
||||
<Rule Id="CA2224" Action="Error" />
|
||||
<Rule Id="CA2225" Action="Error" />
|
||||
<Rule Id="CA2226" Action="Error" />
|
||||
<Rule Id="CA2227" Action="Error" />
|
||||
<Rule Id="CA2228" Action="Error" />
|
||||
<Rule Id="CA2229" Action="Error" />
|
||||
<Rule Id="CA2230" Action="Error" />
|
||||
<Rule Id="CA2231" Action="Error" />
|
||||
<Rule Id="CA2232" Action="Error" />
|
||||
<Rule Id="CA2233" Action="Error" />
|
||||
<Rule Id="CA2234" Action="Error" />
|
||||
<Rule Id="CA2235" Action="Error" />
|
||||
<Rule Id="CA2236" Action="Error" />
|
||||
<Rule Id="CA2237" Action="Error" />
|
||||
<Rule Id="CA2238" Action="Error" />
|
||||
<Rule Id="CA2239" Action="Error" />
|
||||
<Rule Id="CA2240" Action="Error" />
|
||||
<Rule Id="CA2241" Action="Error" />
|
||||
<Rule Id="CA2242" Action="Error" />
|
||||
<Rule Id="CA2243" Action="Error" />
|
||||
<Rule Id="CA5122" Action="Error" />
|
||||
</Rules>
|
||||
</RuleSet>
|
@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(ArtifactsDir)' != ''">
|
||||
<OutputPath Condition="$(UseBinPath) == ''">$(ArtifactsDir)\$(MSBuildProjectName)</OutputPath>
|
||||
<OutputPath Condition="$(UseBinPath) == 'true'">$(ArtifactsDir)\$(MSBuildProjectName)\bin</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Microsoft.AspNet.SignalR.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis Condition="'$(RunCodeAnalysis)' == ''">false</RunCodeAnalysis>
|
||||
<NoWarn>1591</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RunCodeAnalysis)' == 'true'">
|
||||
<DefineConstants>$(DefineConstants);CODE_ANALYSIS</DefineConstants>
|
||||
<VisualStudioVersion>11.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(OS)' != 'Windows_NT'">
|
||||
<DefineConstants>$(DefineConstants);MONO</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(KeyFile)' != '' And '$(DisableSigning)' != 'true'">
|
||||
<DefineConstants>$(DefineConstants);SIGNED</DefineConstants>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(KeyFile)</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="Exists('$(MSBuildThisFileDirectory)GlobalSuppressions.cs')">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)GlobalSuppressions.cs">
|
||||
<Link>GlobalSuppressions.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<CodeAnalysisDictionary Include="$(MSBuildThisFileDirectory)CodeAnalysisDictionary.xml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,6 +1,13 @@
|
||||
<Project>
|
||||
<!-- Common to all Sonarr Projects -->
|
||||
<PropertyGroup>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||
<RuntimeIdentifiers>win-x64;win-x86;osx-x64;linux-x64;linux-musl-x64;linux-arm;linux-arm64;linux-musl-arm64</RuntimeIdentifiers>
|
||||
|
||||
<SonarrRootDir>$(MSBuildThisFileDirectory)..\</SonarrRootDir>
|
||||
|
||||
<!-- Specifies the type of output -->
|
||||
@ -17,6 +24,10 @@
|
||||
<SonarrProject Condition="$(MSBuildProjectName.StartsWith('Sonarr'))">true</SonarrProject>
|
||||
<SonarrProject Condition="$(MSBuildProjectName.StartsWith('ServiceInstall'))">true</SonarrProject>
|
||||
<SonarrProject Condition="$(MSBuildProjectName.StartsWith('ServiceUninstall'))">true</SonarrProject>
|
||||
|
||||
<!-- A test project gets the test sdk packages automatically added -->
|
||||
<TestProject>false</TestProject>
|
||||
<TestProject Condition="$(MSBuildProjectName.EndsWith('.Test'))">true</TestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@ -32,12 +43,11 @@
|
||||
<OutputPath Condition="'$(SonarrOutputType)'=='Update'">$(SonarrRootDir)_output\Sonarr.Update\</OutputPath>
|
||||
|
||||
<!-- Paths relative to project file for better readability -->
|
||||
<EnableBaseIntermediateOutputPathMismatchWarning>false</EnableBaseIntermediateOutputPathMismatchWarning>
|
||||
<BaseIntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)'))</IntermediateOutputPath>
|
||||
<OutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(OutputPath)'))</OutputPath>
|
||||
|
||||
<!-- below net4.7.1 the new portable pdb format has no line numbers, pdb to mdb probably doesn't like it either -->
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -45,17 +55,19 @@
|
||||
<PropertyGroup Condition="'$(SonarrOutputType)'=='Test'">
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<SelfContained>false</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Set the Product and Version info for our own projects -->
|
||||
<PropertyGroup Condition="'$(SonarrProject)'=='true'">
|
||||
<Product>Sonarr</Product>
|
||||
<Company>sonarr.tv</Company>
|
||||
<Copyright>Copyright 2010-$([System.DateTime]::Now.ToString('yyyy')) sonarr.tv (GNU General Public v3)</Copyright>
|
||||
<Copyright>Copyright 2014-$([System.DateTime]::Now.ToString('yyyy')) sonarr.tv (GNU General Public v3)</Copyright>
|
||||
|
||||
<!-- Should be replaced by CI -->
|
||||
<AssemblyVersion>10.0.0.*</AssemblyVersion>
|
||||
<AssemblyConfiguration>$(Configuration)-dev</AssemblyConfiguration>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
|
||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
|
||||
@ -71,10 +83,64 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- We don't want separate framework directories till we go dotnet core -->
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
|
||||
<!-- For now keep the NzbDrone namespace -->
|
||||
<RootNamespace Condition="'$(SonarrProject)'=='true'">$(MSBuildProjectName.Replace('Sonarr','NzbDrone'))</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Standard testing packages -->
|
||||
<ItemGroup Condition="'$(TestProject)'=='true'">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NunitXml.TestLogger" Version="2.1.62" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TestProject)'=='true' and '$(TargetFramework)'=='net6.0'">
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(SonarrProject)'=='true' and '$(EnableAnalyzers)'=='false'">
|
||||
<!-- FXCop Built into Net5 SDK now as NETAnalyzers, Enabled by default on net5 projects -->
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
Set runtime identifier to local system type if not specified
|
||||
-->
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' == 'Windows_NT'">
|
||||
<PropertyGroup>
|
||||
<IsWindows>true</IsWindows>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<When Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
|
||||
<PropertyGroup>
|
||||
<IsOSX>true</IsOSX>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<IsLinux>true</IsLinux>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<PropertyGroup Condition="'$(IsWindows)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsLinux)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsOSX)' == 'true' and
|
||||
'$(RuntimeIdentifier)' == ''">
|
||||
<_UsingDefaultRuntimeIdentifier>true</_UsingDefaultRuntimeIdentifier>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
4
src/Directory.Build.targets
Normal file
4
src/Directory.Build.targets
Normal file
@ -0,0 +1,4 @@
|
||||
<Project>
|
||||
<Import Project="Targets/PublishAllRids.targets" />
|
||||
<Import Project="Targets/CopyRuntimes.targets" />
|
||||
</Project>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
Copied from mono/4.5/Facades of the mono 5.4 release.
|
||||
These are the mono version of the dotnet Core TypeForwardedTo assemblies.
|
||||
Using these assemblies is no longer necessary once we reach mono 5.18 as minimum version
|
@ -38,15 +38,6 @@ public class DataMapper : IDataMapper
|
||||
|
||||
private DbCommand _command;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a DataMapper for the given provider type and connection string.
|
||||
/// </summary>
|
||||
/// <param name="providerName">Ex: </param>
|
||||
/// <param name="connectionString">The database connection string.</param>
|
||||
public DataMapper(string providerName, string connectionString)
|
||||
: this(DbProviderFactories.GetFactory(providerName), connectionString)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// A database provider agnostic initialization.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
|
||||
<AssemblyVersion>3.17.0.0</AssemblyVersion>
|
||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||
|
@ -1,10 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBuilder" Version="6.0.0" />
|
||||
<PackageReference Include="NBuilder" Version="6.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Core\Sonarr.Core.csproj" />
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NLog;
|
||||
@ -9,7 +10,7 @@
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Firefox;
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using OpenQA.Selenium.Remote;
|
||||
|
||||
namespace NzbDrone.Automation.Test
|
||||
@ -34,7 +35,14 @@ public AutomationTest()
|
||||
[OneTimeSetUp]
|
||||
public void SmokeTestSetup()
|
||||
{
|
||||
driver = new FirefoxDriver();
|
||||
var options = new ChromeOptions();
|
||||
options.AddArguments("--headless");
|
||||
var service = ChromeDriverService.CreateDefaultService();
|
||||
|
||||
// Timeout as windows automation tests seem to take alot longer to get going
|
||||
driver = new ChromeDriver(service, options, new TimeSpan(0, 3, 0));
|
||||
|
||||
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
|
||||
|
||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
||||
_runner.KillAll();
|
||||
@ -56,6 +64,19 @@ protected IEnumerable<string> GetPageErrors()
|
||||
.Select(e => e.Text);
|
||||
}
|
||||
|
||||
protected void TakeScreenshot(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
Screenshot image = ((ITakesScreenshot)driver).GetScreenshot();
|
||||
image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to save screenshot {name}, {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void SmokeTestTearDown()
|
||||
{
|
||||
|
@ -1,11 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Selenium.Firefox.WebDriver" Version="0.24.0" />
|
||||
<PackageReference Include="Selenium.Support" Version="3.141.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="88.0.4324.9600" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Sonarr.Test.Common.csproj" />
|
||||
|
@ -439,7 +439,7 @@ public void CopyFolder_should_detect_caseinsensitive_folder()
|
||||
[Test]
|
||||
public void CopyFolder_should_not_copy_casesensitive_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
@ -540,9 +540,10 @@ public void MoveFolder_should_rename_caseinsensitive_folder()
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Platform(Exclude = "MacOsX")]
|
||||
public void MoveFolder_should_rename_casesensitive_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
WithRealDiskProvider();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
@ -19,7 +19,7 @@ public void EnsureWindowsPath(string path)
|
||||
[TestCase(@"/var/user/file with, comma.mkv")]
|
||||
public void EnsureLinuxPath(string path)
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
}
|
||||
}
|
||||
|
@ -250,11 +250,6 @@ public void should_not_follow_redirects()
|
||||
[Test]
|
||||
public void should_follow_redirects_to_https()
|
||||
{
|
||||
if (typeof(TDispatcher) == typeof(ManagedHttpDispatcher) && PlatformInfo.IsMono)
|
||||
{
|
||||
Assert.Ignore("Will fail on tls1.2 via managed dispatcher, ignore.");
|
||||
}
|
||||
|
||||
var request = new HttpRequestBuilder($"http://{_httpBinHost}/redirect-to")
|
||||
.AddQueryParam("url", $"https://sonarr.tv/")
|
||||
.Build();
|
||||
@ -758,7 +753,7 @@ public void should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = $"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeUriString(malformedCookie)}";
|
||||
string url = $"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeDataString(malformedCookie)}";
|
||||
|
||||
var requestSet = new HttpRequest(url);
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
|
@ -52,7 +52,7 @@ public void Clean_Path_Windows(string dirty, string clean)
|
||||
[TestCase(@"//CAPITAL//lower// ", @"/CAPITAL/lower")]
|
||||
public void Clean_Path_Linux(string dirty, string clean)
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
var result = dirty.CleanFilePath();
|
||||
result.Should().Be(clean);
|
||||
@ -152,14 +152,14 @@ public void path_should_return_parent_windows(string path, string parentPath)
|
||||
[TestCase(@"/test", "/")]
|
||||
public void path_should_return_parent_mono(string path, string parentPath)
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
path.GetParentPath().Should().Be(parentPath);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void path_should_return_parent_for_oversized_path()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
// This test will fail on Windows if long path support is not enabled: https://www.howtogeek.com/266621/how-to-make-windows-10-accept-file-paths-over-260-characters/
|
||||
// It will also fail if the app isn't configured to use long path (such as resharper): https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/
|
||||
@ -223,7 +223,7 @@ public void get_actual_casing_should_return_actual_casing_for_local_file_in_wind
|
||||
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\").Replace("system32", "System32");
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\", "C:\\").Replace("d:\\", "D:\\").Replace("system32", "System32");
|
||||
|
||||
path.ToUpper().GetActualCasing().Should().Be(path);
|
||||
path.ToLower().GetActualCasing().Should().Be(path);
|
||||
@ -232,7 +232,7 @@ public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windo
|
||||
[Test]
|
||||
public void get_actual_casing_should_return_original_value_in_linux()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
var path = Directory.GetCurrentDirectory();
|
||||
path.GetActualCasing().Should().Be(path);
|
||||
path.GetActualCasing().Should().Be(path);
|
||||
@ -280,7 +280,7 @@ public void GetUpdateClientFolder()
|
||||
[Test]
|
||||
public void GetUpdateClientExePath()
|
||||
{
|
||||
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\sonarr_update\Sonarr.Update.exe".AsOsAgnostic());
|
||||
GetIAppDirectoryInfo().GetUpdateClientExePath(PlatformType.DotNet).Should().BeEquivalentTo(@"C:\Temp\sonarr_update\Sonarr.Update.exe".AsOsAgnostic());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -306,7 +306,7 @@ public void GetAncestorFolders_should_return_all_ancestors_in_path_Windows()
|
||||
[Test]
|
||||
public void GetAncestorFolders_should_return_all_ancestors_in_path_Linux()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
var path = @"/Test/TV/Series Title";
|
||||
var result = path.GetAncestorFolders();
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Model;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Test.Common;
|
||||
@ -13,6 +14,7 @@
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
[NonParallelizable]
|
||||
[TestFixture]
|
||||
public class ProcessProviderFixture : TestBase<ProcessProvider>
|
||||
{
|
||||
@ -65,19 +67,22 @@ public void GetProcessById_should_return_null_for_invalid_process(int processId)
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void Should_be_able_to_start_process()
|
||||
{
|
||||
var process = StartDummyProcess();
|
||||
|
||||
Thread.Sleep(500);
|
||||
|
||||
Subject.Exists(DummyApp.DUMMY_PROCCESS_NAME).Should()
|
||||
.BeTrue("one running dummy process");
|
||||
var check = Subject.GetProcessById(process.Id);
|
||||
check.Should().NotBeNull();
|
||||
|
||||
process.Refresh();
|
||||
process.HasExited.Should().BeFalse();
|
||||
|
||||
process.Kill();
|
||||
process.WaitForExit();
|
||||
|
||||
Subject.Exists(DummyApp.DUMMY_PROCCESS_NAME).Should().BeFalse();
|
||||
process.HasExited.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -131,6 +136,8 @@ public void Should_be_able_to_start_python()
|
||||
|
||||
|
||||
[Test]
|
||||
[Platform(Exclude = "MacOsX")]
|
||||
[Retry(3)]
|
||||
public void kill_all_should_kill_all_process_with_name()
|
||||
{
|
||||
var dummy1 = StartDummyProcess();
|
||||
@ -146,8 +153,33 @@ public void kill_all_should_kill_all_process_with_name()
|
||||
|
||||
private Process StartDummyProcess()
|
||||
{
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, DummyApp.DUMMY_PROCCESS_NAME + ".exe");
|
||||
return Subject.Start(path);
|
||||
var processStarted = new ManualResetEventSlim();
|
||||
|
||||
string suffix;
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
suffix = ".exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix = "";
|
||||
}
|
||||
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, DummyApp.DUMMY_PROCCESS_NAME + suffix);
|
||||
var process = Subject.Start(path, onOutputDataReceived: (string data) =>
|
||||
{
|
||||
if (data.StartsWith("Dummy process. ID:"))
|
||||
{
|
||||
processStarted.Set();
|
||||
}
|
||||
});
|
||||
|
||||
if (!processStarted.Wait(5000))
|
||||
{
|
||||
Assert.Fail("Failed to start process within 2 sec");
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -1,17 +1,18 @@
|
||||
using System;
|
||||
using System.Security.Principal;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
[TestFixture]
|
||||
[Timeout(15000)]
|
||||
public class ServiceProviderTests : TestBase<ServiceProvider>
|
||||
public class ServiceProviderFixture : TestBase<ServiceProvider>
|
||||
{
|
||||
private const string ALWAYS_INSTALLED_SERVICE = "SCardSvr"; //Smart Card
|
||||
private const string TEMP_SERVICE_NAME = "NzbDrone_Nunit";
|
||||
@ -20,6 +21,9 @@ public class ServiceProviderTests : TestBase<ServiceProvider>
|
||||
public void Setup()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Mocker.SetConstant<IProcessProvider>(Mocker.Resolve<ProcessProvider>());
|
||||
|
||||
CleanupService();
|
||||
}
|
||||
|
||||
@ -71,6 +75,7 @@ public void Service_should_be_installed_and_then_uninstalled()
|
||||
Subject.Install(TEMP_SERVICE_NAME);
|
||||
Subject.ServiceExist(TEMP_SERVICE_NAME).Should().BeTrue();
|
||||
Subject.Uninstall(TEMP_SERVICE_NAME);
|
||||
Thread.Sleep(2000);
|
||||
Subject.ServiceExist(TEMP_SERVICE_NAME).Should().BeFalse();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
@ -1,14 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Host\Sonarr.Host.csproj" />
|
||||
<ProjectReference Include="..\NzbDrone.Test.Common\Sonarr.Test.Common.csproj" />
|
||||
<ProjectReference Include="..\NzbDrone.Test.Dummy\Sonarr.Test.Dummy.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -10,6 +10,7 @@
|
||||
namespace NzbDrone.Common.Test.TPLTests
|
||||
{
|
||||
[TestFixture]
|
||||
[Platform(Exclude = "MacOsX")]
|
||||
public class RateLimitServiceFixture : TestBase<RateLimitService>
|
||||
{
|
||||
private DateTime _epoch;
|
||||
|
@ -85,7 +85,9 @@ private void ExtractZip(string compressedFile, string destination)
|
||||
string fullZipToPath = Path.Combine(destination, entryFileName);
|
||||
string directoryName = Path.GetDirectoryName(fullZipToPath);
|
||||
if (directoryName.Length > 0)
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
// Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
|
||||
// of the file, but does not waste memory.
|
||||
@ -103,7 +105,7 @@ private void ExtractTgz(string compressedFile, string destination)
|
||||
Stream inStream = File.OpenRead(compressedFile);
|
||||
Stream gzipStream = new GZipInputStream(inStream);
|
||||
|
||||
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream);
|
||||
TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream, null);
|
||||
tarArchive.ExtractContents(destination);
|
||||
tarArchive.Close();
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using TinyIoC;
|
||||
@ -21,15 +24,74 @@ protected ContainerBuilderBase(IStartupContext args, List<string> assemblies)
|
||||
assemblies.Add(OsInfo.IsWindows ? "Sonarr.Windows" : "Sonarr.Mono");
|
||||
assemblies.Add("Sonarr.Common");
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
var startupPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
foreach (var assemblyName in assemblies)
|
||||
{
|
||||
_loadedTypes.AddRange(Assembly.Load(assembly).GetExportedTypes());
|
||||
_loadedTypes.AddRange(AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(startupPath, $"{assemblyName}.dll")).GetExportedTypes());
|
||||
}
|
||||
|
||||
var toRegisterResolver = new List<string> { "System.Data.SQLite" };
|
||||
toRegisterResolver.AddRange(assemblies.Intersect(new[] { "Sonarr.Core" }));
|
||||
RegisterNativeResolver(toRegisterResolver);
|
||||
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ContainerResolveEventHandler);
|
||||
|
||||
Container = new Container(new TinyIoCContainer(), _loadedTypes);
|
||||
AutoRegisterInterfaces();
|
||||
Container.Register(args);
|
||||
}
|
||||
}
|
||||
|
||||
private static Assembly ContainerResolveEventHandler(object sender, ResolveEventArgs args)
|
||||
{
|
||||
var resolver = new AssemblyDependencyResolver(args.RequestingAssembly.Location);
|
||||
var assemblyPath = resolver.ResolveAssemblyToPath(new AssemblyName(args.Name));
|
||||
|
||||
if (assemblyPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
|
||||
public static void RegisterNativeResolver(IEnumerable<string> assemblyNames)
|
||||
{
|
||||
// This ensures we look for sqlite3 using libsqlite3.so.0 on Linux and not libsqlite3.so which
|
||||
// is less likely to exist.
|
||||
foreach (var name in assemblyNames)
|
||||
{
|
||||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{name}.dll"));
|
||||
|
||||
try
|
||||
{
|
||||
NativeLibrary.SetDllImportResolver(assembly, LoadNativeLib);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// This can only be set once per assembly
|
||||
// Catch required for NzbDrone.Host tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr LoadNativeLib(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath)
|
||||
{
|
||||
var mappedName = libraryName;
|
||||
if (OsInfo.IsLinux)
|
||||
{
|
||||
if (libraryName == "sqlite3")
|
||||
{
|
||||
mappedName = "libsqlite3.so.0";
|
||||
}
|
||||
else if (libraryName == "mediainfo")
|
||||
{
|
||||
mappedName = "libmediainfo.so.0";
|
||||
}
|
||||
}
|
||||
|
||||
return NativeLibrary.Load(mappedName, assembly, dllImportSearchPath);
|
||||
}
|
||||
|
||||
private void AutoRegisterInterfaces()
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ public static byte[] FromBase32String(string str)
|
||||
int bitBufferCount = 0;
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < str.Length;i++ )
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
{
|
||||
bitBuffer = (bitBuffer << 5) | ValidChars.IndexOf(str[i]);
|
||||
bitBufferCount += 5;
|
||||
|
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.17626
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@ -9,6 +9,9 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NzbDrone.Common.EnsureThat.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@ -16,7 +19,7 @@ namespace NzbDrone.Common.EnsureThat.Resources {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class ExceptionMessages {
|
||||
|
@ -48,7 +48,6 @@ public void Register()
|
||||
throw new SonarrStartupException("Cannot create AppFolder, Access to the path {0} is denied", _appFolderInfo.AppDataFolder);
|
||||
}
|
||||
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
SetPermissions();
|
||||
@ -82,8 +81,15 @@ private void MigrateAppDataFolder()
|
||||
|
||||
if (_startupContext.Args.ContainsKey(StartupContext.APPDATA))
|
||||
{
|
||||
if (_diskProvider.FileExists(_appFolderInfo.GetDatabase())) return;
|
||||
if (!_diskProvider.FileExists(oldDbFile)) return;
|
||||
if (_diskProvider.FileExists(_appFolderInfo.GetDatabase()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_diskProvider.FileExists(oldDbFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MoveSqliteDatabase(oldDbFile, _appFolderInfo.GetDatabase());
|
||||
RemovePidFile();
|
||||
@ -125,25 +131,33 @@ private void MigrateAppDataFolder()
|
||||
|
||||
private void InitializeMonoApplicationData()
|
||||
{
|
||||
if (OsInfo.IsWindows) return;
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var configHome = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
if (configHome == "/.config" ||
|
||||
configHome.EndsWith("/.config") && !_diskProvider.FolderExists(configHome.GetParentPath()) ||
|
||||
// It seems that DoNotVerify is the mono behaviour even though .net docs specify a blank string
|
||||
// should be returned if the data doesn't exist. For compatibility with .net core, explicitly
|
||||
// set DoNotVerify (which makes sense given we're explicitly checking that the folder exists)
|
||||
var configHome = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.DoNotVerify);
|
||||
if (configHome.IsNullOrWhiteSpace() ||
|
||||
configHome == "/.config" ||
|
||||
(configHome.EndsWith("/.config") && !_diskProvider.FolderExists(configHome.GetParentPath())) ||
|
||||
!_diskProvider.FolderExists(configHome))
|
||||
{
|
||||
// Tell mono to use appData/.config as ApplicationData folder.
|
||||
// Tell mono/netcore to use appData/.config as ApplicationData folder.
|
||||
Environment.SetEnvironmentVariable("XDG_CONFIG_HOME", Path.Combine(_appFolderInfo.AppDataFolder, ".config"));
|
||||
}
|
||||
|
||||
var dataHome = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
if (dataHome == "/.local/share" ||
|
||||
dataHome.EndsWith("/.local/share") && !_diskProvider.FolderExists(dataHome.GetParentPath().GetParentPath()) ||
|
||||
var dataHome = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.DoNotVerify);
|
||||
if (dataHome.IsNullOrWhiteSpace() ||
|
||||
dataHome == "/.local/share" ||
|
||||
(dataHome.EndsWith("/.local/share") && !_diskProvider.FolderExists(dataHome.GetParentPath().GetParentPath())) ||
|
||||
!_diskProvider.FolderExists(dataHome))
|
||||
{
|
||||
// Tell mono to use appData/.config/share as LocalApplicationData folder.
|
||||
// Tell mono/netcore to use appData/.config/share as LocalApplicationData folder.
|
||||
Environment.SetEnvironmentVariable("XDG_DATA_HOME", Path.Combine(_appFolderInfo.AppDataFolder, ".config/share"));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public interface IAppFolderInfo
|
||||
|
||||
public class AppFolderInfo : IAppFolderInfo
|
||||
{
|
||||
private readonly Environment.SpecialFolder DATA_SPECIAL_FOLDER = Environment.SpecialFolder.CommonApplicationData;
|
||||
private readonly Environment.SpecialFolder _dataSpecialFolder = Environment.SpecialFolder.CommonApplicationData;
|
||||
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(AppFolderInfo));
|
||||
|
||||
@ -24,7 +24,7 @@ public AppFolderInfo(IStartupContext startupContext)
|
||||
{
|
||||
if (OsInfo.IsNotWindows)
|
||||
{
|
||||
DATA_SPECIAL_FOLDER = Environment.SpecialFolder.ApplicationData;
|
||||
_dataSpecialFolder = Environment.SpecialFolder.ApplicationData;
|
||||
}
|
||||
|
||||
if (startupContext.Args.ContainsKey(StartupContext.APPDATA))
|
||||
@ -34,8 +34,8 @@ public AppFolderInfo(IStartupContext startupContext)
|
||||
}
|
||||
else
|
||||
{
|
||||
AppDataFolder = Path.Combine(Environment.GetFolderPath(DATA_SPECIAL_FOLDER, Environment.SpecialFolderOption.None), "Sonarr");
|
||||
LegacyAppDataFolder = Path.Combine(Environment.GetFolderPath(DATA_SPECIAL_FOLDER, Environment.SpecialFolderOption.None), "NzbDrone");
|
||||
AppDataFolder = Path.Combine(Environment.GetFolderPath(_dataSpecialFolder, Environment.SpecialFolderOption.DoNotVerify), "Sonarr");
|
||||
LegacyAppDataFolder = Path.Combine(Environment.GetFolderPath(_dataSpecialFolder, Environment.SpecialFolderOption.DoNotVerify), "NzbDrone");
|
||||
}
|
||||
|
||||
StartUpFolder = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -30,7 +29,7 @@ static BuildInfo()
|
||||
public static string AppName { get; } = "Sonarr";
|
||||
|
||||
public static Version Version { get; }
|
||||
public static String Branch { get; }
|
||||
public static string Branch { get; }
|
||||
public static string Release { get; }
|
||||
|
||||
public static DateTime BuildDateTime
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
@ -11,10 +12,13 @@ public class OsInfo : IOsInfo
|
||||
public static Os Os { get; }
|
||||
|
||||
public static bool IsNotWindows => !IsWindows;
|
||||
public static bool IsLinux => Os == Os.Linux;
|
||||
public static bool IsLinux => Os == Os.Linux || Os == Os.LinuxMusl || Os == Os.Bsd;
|
||||
public static bool IsOsx => Os == Os.Osx;
|
||||
public static bool IsWindows => Os == Os.Windows;
|
||||
|
||||
// this needs to not be static so we can mock it
|
||||
public bool IsDocker { get; }
|
||||
|
||||
public string Version { get; }
|
||||
public string Name { get; }
|
||||
public string FullName { get; }
|
||||
@ -33,18 +37,7 @@ static OsInfo()
|
||||
case PlatformID.MacOSX:
|
||||
case PlatformID.Unix:
|
||||
{
|
||||
// Sometimes Mac OS reports itself as Unix
|
||||
if (Directory.Exists("/System/Library/CoreServices/") &&
|
||||
(File.Exists("/System/Library/CoreServices/SystemVersion.plist") ||
|
||||
File.Exists("/System/Library/CoreServices/ServerVersion.plist"))
|
||||
)
|
||||
{
|
||||
Os = Os.Osx;
|
||||
}
|
||||
else
|
||||
{
|
||||
Os = Os.Linux;
|
||||
}
|
||||
Os = GetPosixFlavour();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -82,6 +75,64 @@ public OsInfo(IEnumerable<IOsVersionAdapter> versionAdapters, Logger logger)
|
||||
Name = Os.ToString();
|
||||
FullName = Name;
|
||||
}
|
||||
|
||||
if (IsLinux && File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/"))
|
||||
{
|
||||
IsDocker = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static Os GetPosixFlavour()
|
||||
{
|
||||
var output = RunAndCapture("uname", "-s");
|
||||
|
||||
if (output.StartsWith("Darwin"))
|
||||
{
|
||||
return Os.Osx;
|
||||
}
|
||||
else if (output.Contains("BSD"))
|
||||
{
|
||||
return Os.Bsd;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ISMUSL
|
||||
return Os.LinuxMusl;
|
||||
#else
|
||||
return Os.Linux;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private static string RunAndCapture(string filename, string args)
|
||||
{
|
||||
var processStartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = filename,
|
||||
Arguments = args,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
|
||||
var output = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
using (var p = Process.Start(processStartInfo))
|
||||
{
|
||||
// To avoid deadlocks, always read the output stream first and then wait.
|
||||
output = p.StandardOutput.ReadToEnd();
|
||||
|
||||
p.WaitForExit(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
output = string.Empty;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,12 +141,15 @@ public interface IOsInfo
|
||||
string Version { get; }
|
||||
string Name { get; }
|
||||
string FullName { get; }
|
||||
bool IsDocker { get; }
|
||||
}
|
||||
|
||||
public enum Os
|
||||
{
|
||||
Windows,
|
||||
Linux,
|
||||
Osx
|
||||
Osx,
|
||||
LinuxMusl,
|
||||
Bsd
|
||||
}
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
@ -9,7 +6,8 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
public enum PlatformType
|
||||
{
|
||||
DotNet = 0,
|
||||
Mono = 1
|
||||
Mono = 1,
|
||||
NetCore = 2
|
||||
}
|
||||
|
||||
public interface IPlatformInfo
|
||||
@ -19,28 +17,18 @@ public interface IPlatformInfo
|
||||
|
||||
public class PlatformInfo : IPlatformInfo
|
||||
{
|
||||
private static readonly Regex MonoVersionRegex = new Regex(@"(?<=\W|^)(?<version>\d+\.\d+(\.\d+)?(\.\d+)?)(?=\W)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static PlatformType _platform;
|
||||
private static Version _version;
|
||||
|
||||
static PlatformInfo()
|
||||
{
|
||||
if (Type.GetType("Mono.Runtime") != null)
|
||||
{
|
||||
_platform = PlatformType.Mono;
|
||||
_version = GetMonoVersion();
|
||||
}
|
||||
else
|
||||
{
|
||||
_platform = PlatformType.DotNet;
|
||||
_version = GetDotNetVersion();
|
||||
}
|
||||
_platform = PlatformType.NetCore;
|
||||
_version = Environment.Version;
|
||||
}
|
||||
|
||||
public static PlatformType Platform => _platform;
|
||||
public static bool IsMono => Platform == PlatformType.Mono;
|
||||
public static bool IsDotNet => Platform == PlatformType.DotNet;
|
||||
public static bool IsNetCore => Platform == PlatformType.NetCore;
|
||||
|
||||
public static string PlatformName
|
||||
{
|
||||
@ -50,8 +38,10 @@ public static string PlatformName
|
||||
{
|
||||
return ".NET";
|
||||
}
|
||||
|
||||
return "Mono";
|
||||
else
|
||||
{
|
||||
return ".NET Core";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,98 +51,5 @@ public static Version GetVersion()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
private static Version GetMonoVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = Type.GetType("Mono.Runtime");
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
var displayNameMethod = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (displayNameMethod != null)
|
||||
{
|
||||
var displayName = displayNameMethod.Invoke(null, null).ToString();
|
||||
var versionMatch = MonoVersionRegex.Match(displayName);
|
||||
|
||||
if (versionMatch.Success)
|
||||
{
|
||||
return new Version(versionMatch.Groups["version"].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Couldnt get Mono version: " + ex.ToString());
|
||||
}
|
||||
|
||||
return new Version();
|
||||
}
|
||||
|
||||
private static Version GetDotNetVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
|
||||
using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
|
||||
{
|
||||
if (ndpKey == null)
|
||||
{
|
||||
return new Version(4, 0);
|
||||
}
|
||||
|
||||
var releaseKey = (int)ndpKey.GetValue("Release");
|
||||
|
||||
if (releaseKey >= 528040)
|
||||
{
|
||||
return new Version(4, 8, 0);
|
||||
}
|
||||
if (releaseKey >= 461808)
|
||||
{
|
||||
return new Version(4, 7, 2);
|
||||
}
|
||||
if (releaseKey >= 461308)
|
||||
{
|
||||
return new Version(4, 7, 1);
|
||||
}
|
||||
if (releaseKey >= 460798)
|
||||
{
|
||||
return new Version(4, 7);
|
||||
}
|
||||
if (releaseKey >= 394802)
|
||||
{
|
||||
return new Version(4, 6, 2);
|
||||
}
|
||||
if (releaseKey >= 394254)
|
||||
{
|
||||
return new Version(4, 6, 1);
|
||||
}
|
||||
if (releaseKey >= 393295)
|
||||
{
|
||||
return new Version(4, 6);
|
||||
}
|
||||
if (releaseKey >= 379893)
|
||||
{
|
||||
return new Version(4, 5, 2);
|
||||
}
|
||||
if (releaseKey >= 378675)
|
||||
{
|
||||
return new Version(4, 5, 1);
|
||||
}
|
||||
if (releaseKey >= 378389)
|
||||
{
|
||||
return new Version(4, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Couldnt get .NET framework version: " + ex.ToString());
|
||||
}
|
||||
|
||||
return new Version(4, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,13 +23,15 @@ public RuntimeInfo(IServiceProvider serviceProvider, Logger logger)
|
||||
serviceProvider.ServiceExist(ServiceProvider.SERVICE_NAME) &&
|
||||
serviceProvider.GetStatus(ServiceProvider.SERVICE_NAME) == ServiceControllerStatus.StartPending;
|
||||
|
||||
//Guarded to avoid issues when running in a non-managed process
|
||||
var entry = Assembly.GetEntryAssembly();
|
||||
// net6.0 will return Sonarr.dll for entry assembly, we need the actual
|
||||
// executable name (Sonarr on linux). On mono this will return the location of
|
||||
// the mono executable itself, which is not what we want.
|
||||
var entry = Process.GetCurrentProcess().MainModule;
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
ExecutingApplication = entry.Location;
|
||||
IsWindowsTray = OsInfo.IsWindows && entry.ManifestModule.Name == $"{ProcessProvider.SONARR_PROCESS_NAME}.exe";
|
||||
ExecutingApplication = entry.FileName;
|
||||
IsWindowsTray = OsInfo.IsWindows && entry.ModuleName == $"{ProcessProvider.SONARR_PROCESS_NAME}.exe";
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +65,11 @@ public bool IsAdmin
|
||||
{
|
||||
get
|
||||
{
|
||||
if (OsInfo.IsNotWindows)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
|
||||
@ -124,10 +131,25 @@ private static bool InternalIsTesting()
|
||||
{
|
||||
var lowerProcessName = Process.GetCurrentProcess().ProcessName.ToLower();
|
||||
|
||||
if (lowerProcessName.Contains("vshost")) return true;
|
||||
if (lowerProcessName.Contains("nunit")) return true;
|
||||
if (lowerProcessName.Contains("jetbrain")) return true;
|
||||
if (lowerProcessName.Contains("resharper")) return true;
|
||||
if (lowerProcessName.Contains("vshost"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerProcessName.Contains("nunit"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerProcessName.Contains("jetbrain"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerProcessName.Contains("resharper"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -137,24 +159,50 @@ private static bool InternalIsTesting()
|
||||
try
|
||||
{
|
||||
var currentAssemblyLocation = typeof(RuntimeInfo).Assembly.Location;
|
||||
if (currentAssemblyLocation.ToLower().Contains("_output")) return true;
|
||||
if (currentAssemblyLocation.ToLower().Contains("_output"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentAssemblyLocation.ToLower().Contains("_tests"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
|
||||
if (lowerCurrentDir.Contains("teamcity")) return true;
|
||||
if (lowerCurrentDir.Contains("buildagent")) return true;
|
||||
if (lowerCurrentDir.Contains("_output")) return true;
|
||||
if (lowerCurrentDir.Contains("vsts"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerCurrentDir.Contains("buildagent"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerCurrentDir.Contains("_output"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerCurrentDir.Contains("_tests"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool InternalIsDebug()
|
||||
{
|
||||
if (BuildInfo.IsDebug || Debugger.IsAttached) return true;
|
||||
if (BuildInfo.IsDebug || Debugger.IsAttached)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -162,7 +210,10 @@ private static bool InternalIsDebug()
|
||||
private static bool InternalIsOfficialBuild()
|
||||
{
|
||||
//Official builds will never have such a high revision
|
||||
if (BuildInfo.Version.Major >= 10 || BuildInfo.Version.Revision > 10000) return false;
|
||||
if (BuildInfo.Version.Major >= 10 || BuildInfo.Version.Revision > 10000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ public class StartupContext : IStartupContext
|
||||
public const string TERMINATE = "terminateexisting";
|
||||
public const string RESTART = "restart";
|
||||
public const string REGISTER_URL = "registerurl";
|
||||
public const string NO_SINGLE_INSTANCE_CHECK = "nosingleinstancecheck";
|
||||
public const string EXIT_IMMEDIATELY = "exitimmediately";
|
||||
|
||||
public StartupContext(params string[] args)
|
||||
|
@ -6,16 +6,17 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default(TValue))
|
||||
{
|
||||
TValue value;
|
||||
return dictionary.TryGetValue(key, out value) ? value : defaultValue;
|
||||
}
|
||||
|
||||
public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
if (first == null)
|
||||
{
|
||||
throw new ArgumentNullException("first");
|
||||
}
|
||||
|
||||
if (second == null)
|
||||
{
|
||||
throw new ArgumentNullException("second");
|
||||
}
|
||||
|
||||
var merged = new Dictionary<T1, T2>();
|
||||
first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
|
||||
@ -28,5 +29,19 @@ public static void Add<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>
|
||||
{
|
||||
collection.Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
}
|
||||
|
||||
public static IDictionary<TNewKey, TNewValue> SelectDictionary<TKey, TValue, TNewKey, TNewValue>(this IDictionary<TKey, TValue> dictionary,
|
||||
Func<KeyValuePair<TKey, TValue>, ValueTuple<TNewKey, TNewValue>> selection)
|
||||
{
|
||||
return dictionary.Select(selection).ToDictionary(t => t.Item1, t => t.Item2);
|
||||
}
|
||||
|
||||
public static IDictionary<TNewKey, TNewValue> SelectDictionary<TKey, TValue, TNewKey, TNewValue>(
|
||||
this IDictionary<TKey, TValue> dictionary,
|
||||
Func<KeyValuePair<TKey, TValue>, TNewKey> keySelector,
|
||||
Func<KeyValuePair<TKey, TValue>, TNewValue> valueSelector)
|
||||
{
|
||||
return dictionary.SelectDictionary(p => { return (keySelector(p), valueSelector(p)); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,6 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
var knownKeys = new HashSet<TKey>();
|
||||
|
||||
return source.Where(element => knownKeys.Add(keySelector(element)));
|
||||
}
|
||||
|
||||
public static IEnumerable<TFirst> IntersectBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first, Func<TFirst, TKey> firstKeySelector,
|
||||
IEnumerable<TSecond> second, Func<TSecond, TKey> secondKeySelector,
|
||||
IEqualityComparer<TKey> keyComparer)
|
||||
@ -117,5 +110,14 @@ public static List<TResult> SelectList<TSource, TResult>(this IEnumerable<TSourc
|
||||
//
|
||||
// return descending ? source.OrderByDescending(orderByFunc) : source.OrderBy(orderByFunc);
|
||||
// }
|
||||
public static string ConcatToString<TSource>(this IEnumerable<TSource> source, string separator = ", ")
|
||||
{
|
||||
return string.Join(separator, source.Select(x => x.ToString()));
|
||||
}
|
||||
|
||||
public static string ConcatToString<TSource>(this IEnumerable<TSource> source, Func<TSource, string> predicate, string separator = ", ")
|
||||
{
|
||||
return string.Join(separator, source.Select(predicate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public static class PathExtensions
|
||||
private const string DB_RESTORE = "sonarr.restore";
|
||||
private const string LOG_DB = "logs.db";
|
||||
private const string NLOG_CONFIG_FILE = "nlog.config";
|
||||
private const string UPDATE_CLIENT_EXE = "Sonarr.Update.exe";
|
||||
private const string UPDATE_CLIENT_EXE_NAME = "Sonarr.Update";
|
||||
|
||||
private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "sonarr_update" + Path.DirectorySeparatorChar;
|
||||
private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "Sonarr" + Path.DirectorySeparatorChar;
|
||||
@ -34,7 +34,8 @@ public static string CleanFilePath(this string path)
|
||||
|
||||
var info = new FileInfo(path.Trim());
|
||||
|
||||
if (OsInfo.IsWindows && info.FullName.StartsWith(@"\\")) //UNC
|
||||
//UNC
|
||||
if (OsInfo.IsWindows && info.FullName.StartsWith(@"\\"))
|
||||
{
|
||||
return info.FullName.TrimEnd('/', '\\', ' ');
|
||||
}
|
||||
@ -54,7 +55,11 @@ public static bool PathEquals(this string firstPath, string secondPath, StringCo
|
||||
comparison = DiskProviderBase.PathStringComparison;
|
||||
}
|
||||
|
||||
if (firstPath.Equals(secondPath, comparison.Value)) return true;
|
||||
if (firstPath.Equals(secondPath, comparison.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), comparison.Value);
|
||||
}
|
||||
|
||||
@ -260,6 +265,21 @@ public static string GetLongestCommonPath(this List<string> paths)
|
||||
return substring.Substring(0, lastSeparatorIndex);
|
||||
}
|
||||
|
||||
public static string ProcessNameToExe(this string processName, PlatformType runtime)
|
||||
{
|
||||
if (OsInfo.IsWindows || runtime != PlatformType.NetCore)
|
||||
{
|
||||
processName += ".exe";
|
||||
}
|
||||
|
||||
return processName;
|
||||
}
|
||||
|
||||
public static string ProcessNameToExe(this string processName)
|
||||
{
|
||||
return processName.ProcessNameToExe(PlatformInfo.Platform);
|
||||
}
|
||||
|
||||
public static string GetAppDataPath(this IAppFolderInfo appFolderInfo)
|
||||
{
|
||||
return appFolderInfo.AppDataFolder;
|
||||
@ -320,9 +340,9 @@ public static string GetUpdateClientFolder(this IAppFolderInfo appFolderInfo)
|
||||
return Path.Combine(GetUpdatePackageFolder(appFolderInfo), UPDATE_CLIENT_FOLDER_NAME);
|
||||
}
|
||||
|
||||
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo)
|
||||
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo, PlatformType runtime)
|
||||
{
|
||||
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE);
|
||||
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE_NAME).ProcessNameToExe(runtime);
|
||||
}
|
||||
|
||||
public static string GetDatabase(this IAppFolderInfo appFolderInfo)
|
||||
|
@ -11,7 +11,7 @@ public static string CalculateCrc(string input)
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(input);
|
||||
foreach (byte myByte in bytes)
|
||||
{
|
||||
mCrc ^= ((uint)(myByte) << 24);
|
||||
mCrc ^= (uint)myByte << 24;
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
if ((Convert.ToUInt32(mCrc) & 0x80000000) == 0x80000000)
|
||||
|
@ -33,19 +33,10 @@ public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
|
||||
|
||||
if (PlatformInfo.IsMono && request.ResponseStream == null)
|
||||
{
|
||||
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.None;
|
||||
webRequest.Headers.Add("Accept-Encoding", "gzip");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
}
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
|
||||
webRequest.Method = request.Method.ToString();
|
||||
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
||||
@ -121,29 +112,7 @@ public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request.ResponseStream != null && httpWebResponse.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
// A target ResponseStream was specified, write to that instead.
|
||||
// But only on the OK status code, since we don't want to write failures and redirects.
|
||||
responseStream.CopyTo(request.ResponseStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = responseStream.ToBytes();
|
||||
|
||||
if (PlatformInfo.IsMono && httpWebResponse.ContentEncoding == "gzip")
|
||||
{
|
||||
using (var compressedStream = new MemoryStream(data))
|
||||
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))
|
||||
using (var decompressedStream = new MemoryStream())
|
||||
{
|
||||
gzip.CopyTo(decompressedStream);
|
||||
data = decompressedStream.ToArray();
|
||||
}
|
||||
|
||||
httpWebResponse.Headers.Remove("Content-Encoding");
|
||||
}
|
||||
}
|
||||
data = responseStream.ToBytes();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ public sealed class HttpAccept
|
||||
{
|
||||
public static readonly HttpAccept Rss = new HttpAccept("application/rss+xml, text/rss+xml, application/xml, text/xml");
|
||||
public static readonly HttpAccept Json = new HttpAccept("application/json");
|
||||
public static readonly HttpAccept JsonCharset = new HttpAccept("application/json; charset=utf-8");
|
||||
public static readonly HttpAccept Html = new HttpAccept("text/html");
|
||||
|
||||
public string Value { get; private set; }
|
||||
|
@ -32,7 +32,10 @@ private static void HandleAppDomainException(object sender, UnhandledExceptionEv
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
|
||||
if (exception == null) return;
|
||||
if (exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is NullReferenceException &&
|
||||
exception.ToString().Contains("Microsoft.AspNet.SignalR.Transports.TransportHeartbeat.ProcessServerCommand"))
|
||||
@ -41,16 +44,6 @@ private static void HandleAppDomainException(object sender, UnhandledExceptionEv
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlatformInfo.IsMono)
|
||||
{
|
||||
if (exception is TypeInitializationException && exception.InnerException is DllNotFoundException ||
|
||||
exception is DllNotFoundException)
|
||||
{
|
||||
Logger.Debug(exception, "Minor Fail: " + exception.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("EPIC FAIL: {0}", exception);
|
||||
Logger.Fatal(exception, "EPIC FAIL.");
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using NLog;
|
||||
using NLog.Common;
|
||||
@ -22,7 +21,8 @@ public class SentryTarget : TargetWithLayout
|
||||
// don't report uninformative SQLite exceptions
|
||||
// busy/locked are benign https://forums.sonarr.tv/t/owin-sqlite-error-5-database-is-locked/5423/11
|
||||
// The others will be user configuration problems and silt up Sentry
|
||||
private static readonly HashSet<SQLiteErrorCode> FilteredSQLiteErrors = new HashSet<SQLiteErrorCode> {
|
||||
private static readonly HashSet<SQLiteErrorCode> FilteredSQLiteErrors = new HashSet<SQLiteErrorCode>
|
||||
{
|
||||
SQLiteErrorCode.Busy,
|
||||
SQLiteErrorCode.Locked,
|
||||
SQLiteErrorCode.Perm,
|
||||
@ -36,7 +36,8 @@ public class SentryTarget : TargetWithLayout
|
||||
|
||||
// use string and not Type so we don't need a reference to the project
|
||||
// where these are defined
|
||||
private static readonly HashSet<string> FilteredExceptionTypeNames = new HashSet<string> {
|
||||
private static readonly HashSet<string> FilteredExceptionTypeNames = new HashSet<string>
|
||||
{
|
||||
// UnauthorizedAccessExceptions will just be user configuration issues
|
||||
"UnauthorizedAccessException",
|
||||
// Filter out people stuck in boot loops
|
||||
@ -45,49 +46,51 @@ public class SentryTarget : TargetWithLayout
|
||||
"TinyIoCResolutionException"
|
||||
};
|
||||
|
||||
public static readonly List<string> FilteredExceptionMessages = new List<string> {
|
||||
public static readonly List<string> FilteredExceptionMessages = new List<string>
|
||||
{
|
||||
// Swallow the many, many exceptions flowing through from Jackett
|
||||
"Jackett.Common.IndexerException"
|
||||
"Jackett.Common.IndexerException",
|
||||
|
||||
// Fix openflixr being stupid with permissions
|
||||
"openflixr"
|
||||
};
|
||||
|
||||
// exception types in this list will additionally have the exception message added to the
|
||||
// sentry fingerprint. Make sure that this message doesn't vary by exception
|
||||
// (e.g. containing a path or a url) so that the sentry grouping is sensible
|
||||
private static readonly HashSet<string> IncludeExceptionMessageTypes = new HashSet<string> {
|
||||
private static readonly HashSet<string> IncludeExceptionMessageTypes = new HashSet<string>
|
||||
{
|
||||
"SQLiteException"
|
||||
};
|
||||
|
||||
private static readonly IDictionary<LogLevel, SentryLevel> LoggingLevelMap = new Dictionary<LogLevel, SentryLevel>
|
||||
{
|
||||
{LogLevel.Debug, SentryLevel.Debug},
|
||||
{LogLevel.Error, SentryLevel.Error},
|
||||
{LogLevel.Fatal, SentryLevel.Fatal},
|
||||
{LogLevel.Info, SentryLevel.Info},
|
||||
{LogLevel.Trace, SentryLevel.Debug},
|
||||
{LogLevel.Warn, SentryLevel.Warning},
|
||||
{ LogLevel.Debug, SentryLevel.Debug },
|
||||
{ LogLevel.Error, SentryLevel.Error },
|
||||
{ LogLevel.Fatal, SentryLevel.Fatal },
|
||||
{ LogLevel.Info, SentryLevel.Info },
|
||||
{ LogLevel.Trace, SentryLevel.Debug },
|
||||
{ LogLevel.Warn, SentryLevel.Warning },
|
||||
};
|
||||
|
||||
private static readonly IDictionary<LogLevel, BreadcrumbLevel> BreadcrumbLevelMap = new Dictionary<LogLevel, BreadcrumbLevel>
|
||||
{
|
||||
{LogLevel.Debug, BreadcrumbLevel.Debug},
|
||||
{LogLevel.Error, BreadcrumbLevel.Error},
|
||||
{LogLevel.Fatal, BreadcrumbLevel.Critical},
|
||||
{LogLevel.Info, BreadcrumbLevel.Info},
|
||||
{LogLevel.Trace, BreadcrumbLevel.Debug},
|
||||
{LogLevel.Warn, BreadcrumbLevel.Warning},
|
||||
{ LogLevel.Debug, BreadcrumbLevel.Debug },
|
||||
{ LogLevel.Error, BreadcrumbLevel.Error },
|
||||
{ LogLevel.Fatal, BreadcrumbLevel.Critical },
|
||||
{ LogLevel.Info, BreadcrumbLevel.Info },
|
||||
{ LogLevel.Trace, BreadcrumbLevel.Debug },
|
||||
{ LogLevel.Warn, BreadcrumbLevel.Warning },
|
||||
};
|
||||
|
||||
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||
private readonly IDisposable _sdk;
|
||||
private bool _disposed;
|
||||
|
||||
private readonly SentryDebounce _debounce;
|
||||
|
||||
private bool _disposed;
|
||||
private bool _unauthorized;
|
||||
|
||||
public bool FilterEvents { get; set; }
|
||||
public Version DatabaseVersion { get; set; }
|
||||
public int DatabaseMigration { get; set; }
|
||||
|
||||
public bool SentryEnabled { get; set; }
|
||||
|
||||
public SentryTarget(string dsn)
|
||||
@ -98,7 +101,6 @@ public SentryTarget(string dsn)
|
||||
o.AttachStacktrace = true;
|
||||
o.MaxBreadcrumbs = 200;
|
||||
o.SendDefaultPii = false;
|
||||
o.AttachStacktrace = true;
|
||||
o.Debug = false;
|
||||
o.DiagnosticsLevel = SentryLevel.Debug;
|
||||
o.Release = BuildInfo.Release;
|
||||
@ -136,11 +138,6 @@ public void InitializeScope()
|
||||
|
||||
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
|
||||
scope.SetTag("branch", BuildInfo.Branch);
|
||||
|
||||
if (DatabaseVersion != default(Version))
|
||||
{
|
||||
scope.SetTag("sqlite_version", $"{DatabaseVersion}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -148,12 +145,20 @@ public void UpdateScope(IOsInfo osInfo)
|
||||
{
|
||||
SentrySdk.ConfigureScope(scope =>
|
||||
{
|
||||
if (osInfo.Name != null && PlatformInfo.IsMono)
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateScope(Version databaseVersion, int migration, string updateBranch, IPlatformInfo platformInfo)
|
||||
{
|
||||
SentrySdk.ConfigureScope(scope =>
|
||||
{
|
||||
scope.Environment = updateBranch;
|
||||
scope.SetTag("runtime_version", $"{PlatformInfo.PlatformName} {platformInfo.Version}");
|
||||
|
||||
if (databaseVersion != default(Version))
|
||||
{
|
||||
// Sentry auto-detection of non-Windows platforms isn't that accurate on certain devices.
|
||||
scope.Contexts.OperatingSystem.Name = osInfo.Name.FirstCharToUpper();
|
||||
scope.Contexts.OperatingSystem.RawDescription = osInfo.FullName;
|
||||
scope.Contexts.OperatingSystem.Version = osInfo.Version.ToString();
|
||||
scope.SetTag("sqlite_version", $"{databaseVersion}");
|
||||
scope.SetTag("database_migration", $"{migration}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -221,7 +226,8 @@ public bool IsSentryMessage(LogEventInfo logEvent)
|
||||
{
|
||||
if (FilterEvents)
|
||||
{
|
||||
if (logEvent.Exception is SQLiteException sqliteException && FilteredSQLiteErrors.Contains(sqliteException.ResultCode))
|
||||
var sqlEx = logEvent.Exception as SQLiteException;
|
||||
if (sqlEx != null && FilteredSQLiteErrors.Contains(sqlEx.ResultCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -14,13 +14,11 @@ public interface IProvidePidFile
|
||||
public class PidFileProvider : IProvidePidFile
|
||||
{
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly IProcessProvider _processProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public PidFileProvider(IAppFolderInfo appFolderInfo, IProcessProvider processProvider, Logger logger)
|
||||
public PidFileProvider(IAppFolderInfo appFolderInfo, Logger logger)
|
||||
{
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_processProvider = processProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -34,11 +32,11 @@ public void Write()
|
||||
var filename = Path.Combine(_appFolderInfo.AppDataFolder, "sonarr.pid");
|
||||
try
|
||||
{
|
||||
File.WriteAllText(filename, _processProvider.GetCurrentProcessId().ToString());
|
||||
File.WriteAllText(filename, ProcessProvider.GetCurrentProcessId().ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to write PID file {0}", filename);
|
||||
_logger.Error(ex, "Unable to write PID file: " + filename);
|
||||
throw new SonarrStartupException(ex, "Unable to write PID file {0}", filename);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ namespace NzbDrone.Common.Processes
|
||||
{
|
||||
public interface IProcessProvider
|
||||
{
|
||||
int GetCurrentProcessId();
|
||||
ProcessInfo GetCurrentProcess();
|
||||
ProcessInfo GetProcessById(int id);
|
||||
List<ProcessInfo> FindProcessByName(string name);
|
||||
@ -43,9 +42,9 @@ public ProcessProvider(Logger logger)
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public int GetCurrentProcessId()
|
||||
public static int GetCurrentProcessId()
|
||||
{
|
||||
return Process.GetCurrentProcess().Id;
|
||||
return Environment.ProcessId;
|
||||
}
|
||||
|
||||
public ProcessInfo GetCurrentProcess()
|
||||
@ -156,7 +155,10 @@ public Process Start(string path, string args = null, StringDictionary environme
|
||||
|
||||
process.OutputDataReceived += (sender, eventArgs) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventArgs.Data)) return;
|
||||
if (string.IsNullOrWhiteSpace(eventArgs.Data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Debug(eventArgs.Data);
|
||||
|
||||
@ -168,7 +170,10 @@ public Process Start(string path, string args = null, StringDictionary environme
|
||||
|
||||
process.ErrorDataReceived += (sender, eventArgs) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventArgs.Data)) return;
|
||||
if (string.IsNullOrWhiteSpace(eventArgs.Data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Error(eventArgs.Data);
|
||||
|
||||
@ -209,8 +214,11 @@ public Process SpawnNewProcess(string path, string args = null, StringDictionary
|
||||
public ProcessOutput StartAndCapture(string path, string args = null, StringDictionary environmentVariables = null)
|
||||
{
|
||||
var output = new ProcessOutput();
|
||||
var process = Start(path, args, environmentVariables, s => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Standard, s)),
|
||||
error => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Error, error)));
|
||||
var process = Start(path,
|
||||
args,
|
||||
environmentVariables,
|
||||
s => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Standard, s)),
|
||||
error => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Error, error)));
|
||||
|
||||
process.WaitForExit();
|
||||
output.ExitCode = process.ExitCode;
|
||||
@ -249,7 +257,7 @@ public void Kill(int processId)
|
||||
|
||||
process.Refresh();
|
||||
|
||||
if (process.Id != Process.GetCurrentProcess().Id && process.HasExited)
|
||||
if (process.Id != GetCurrentProcessId() && process.HasExited)
|
||||
{
|
||||
_logger.Debug("Process has already exited");
|
||||
return;
|
||||
@ -270,7 +278,7 @@ public void KillAll(string processName)
|
||||
|
||||
foreach (var processInfo in processes)
|
||||
{
|
||||
if (processInfo.Id == Process.GetCurrentProcess().Id)
|
||||
if (processInfo.Id == GetCurrentProcessId())
|
||||
{
|
||||
_logger.Debug("Tried killing own process, skipping: {0} [{1}]", processInfo.Id, processInfo.ProcessName);
|
||||
continue;
|
||||
@ -283,7 +291,10 @@ public void KillAll(string processName)
|
||||
|
||||
private ProcessInfo ConvertToProcessInfo(Process process)
|
||||
{
|
||||
if (process == null) return null;
|
||||
if (process == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
process.Refresh();
|
||||
|
||||
@ -291,14 +302,17 @@ private ProcessInfo ConvertToProcessInfo(Process process)
|
||||
|
||||
try
|
||||
{
|
||||
if (process.Id <= 0) return null;
|
||||
if (process.Id <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
processInfo = new ProcessInfo();
|
||||
processInfo.Id = process.Id;
|
||||
processInfo.Name = process.ProcessName;
|
||||
processInfo.StartPath = GetExeFileName(process);
|
||||
|
||||
if (process.Id != Process.GetCurrentProcess().Id && process.HasExited)
|
||||
if (process.Id != GetCurrentProcessId() && process.HasExited)
|
||||
{
|
||||
processInfo = null;
|
||||
}
|
||||
@ -324,17 +338,7 @@ private static string GetExeFileName(Process process)
|
||||
|
||||
private List<Process> GetProcessesByName(string name)
|
||||
{
|
||||
//TODO: move this to an OS specific class
|
||||
|
||||
var monoProcesses = Process.GetProcessesByName("mono")
|
||||
.Union(Process.GetProcessesByName("mono-sgen"))
|
||||
.Where(process =>
|
||||
process.Modules.Cast<ProcessModule>()
|
||||
.Any(module =>
|
||||
module.ModuleName.ToLower() == name.ToLower() + ".exe"));
|
||||
|
||||
var processes = Process.GetProcessesByName(name)
|
||||
.Union(monoProcesses).ToList();
|
||||
var processes = Process.GetProcessesByName(name).ToList();
|
||||
|
||||
_logger.Debug("Found {0} processes with the name: {1}", processes.Count, name);
|
||||
|
||||
@ -355,15 +359,6 @@ private List<Process> GetProcessesByName(string name)
|
||||
|
||||
private (string Path, string Args) GetPathAndArgs(string path, string args)
|
||||
{
|
||||
if (PlatformInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return ("mono", $"--debug {path} {args}");
|
||||
}
|
||||
if (OsInfo.IsOsx && path.EndsWith(".app", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return ("/usr/bin/open", $"--new {path} --args {args}");
|
||||
}
|
||||
|
||||
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return ("cmd.exe", $"/c {path} {args}");
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration.Install;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.ServiceProcess;
|
||||
@ -64,28 +62,33 @@ public virtual void Install(string serviceName)
|
||||
{
|
||||
_logger.Info("Installing service '{0}'", serviceName);
|
||||
|
||||
var args = $"create {serviceName} " +
|
||||
$"DisplayName= \"{serviceName}\" " +
|
||||
$"binpath= \"{Process.GetCurrentProcess().MainModule.FileName}\" " +
|
||||
"start= auto " +
|
||||
"depend= EventLog/Tcpip/http " +
|
||||
"obj= \"NT AUTHORITY\\LocalService\"";
|
||||
|
||||
var installer = new ServiceProcessInstaller
|
||||
{
|
||||
Account = ServiceAccount.LocalService
|
||||
};
|
||||
_logger.Info(args);
|
||||
|
||||
var serviceInstaller = new ServiceInstaller();
|
||||
var installOutput = _processProvider.StartAndCapture("sc.exe", args);
|
||||
|
||||
if (installOutput.ExitCode != 0)
|
||||
{
|
||||
_logger.Error($"Failed to install service: {installOutput.Lines.Select(x => x.Content).ConcatToString("\n")}");
|
||||
throw new ApplicationException("Failed to install service");
|
||||
}
|
||||
|
||||
string[] cmdline = { @"/assemblypath=" + Process.GetCurrentProcess().MainModule.FileName };
|
||||
_logger.Info(installOutput.Lines.Select(x => x.Content).ConcatToString("\n"));
|
||||
|
||||
var context = new InstallContext("service_install.log", cmdline);
|
||||
serviceInstaller.Context = context;
|
||||
serviceInstaller.DisplayName = serviceName;
|
||||
serviceInstaller.ServiceName = serviceName;
|
||||
serviceInstaller.Description = "Sonarr Application Server";
|
||||
serviceInstaller.StartType = ServiceStartMode.Automatic;
|
||||
serviceInstaller.ServicesDependedOn = new[] { "EventLog", "Tcpip", "http" };
|
||||
var descOutput = _processProvider.StartAndCapture("sc.exe", $"description {serviceName} \"Sonarr Application Server\"");
|
||||
if (descOutput.ExitCode != 0)
|
||||
{
|
||||
_logger.Error($"Failed to install service: {descOutput.Lines.Select(x => x.Content).ConcatToString("\n")}");
|
||||
throw new ApplicationException("Failed to install service");
|
||||
}
|
||||
|
||||
serviceInstaller.Parent = installer;
|
||||
|
||||
serviceInstaller.Install(new ListDictionary());
|
||||
_logger.Info(descOutput.Lines.Select(x => x.Content).ConcatToString("\n"));
|
||||
|
||||
_logger.Info("Service Has installed successfully.");
|
||||
}
|
||||
@ -96,12 +99,8 @@ public virtual void Uninstall(string serviceName)
|
||||
|
||||
Stop(serviceName);
|
||||
|
||||
var serviceInstaller = new ServiceInstaller();
|
||||
|
||||
var context = new InstallContext("service_uninstall.log", null);
|
||||
serviceInstaller.Context = context;
|
||||
serviceInstaller.ServiceName = serviceName;
|
||||
serviceInstaller.Uninstall(null);
|
||||
var output = _processProvider.StartAndCapture("sc.exe", $"delete {serviceName}");
|
||||
_logger.Info(output.Lines.Select(x => x.Content).ConcatToString("\n"));
|
||||
|
||||
_logger.Info("{0} successfully uninstalled", serviceName);
|
||||
}
|
||||
@ -151,7 +150,7 @@ public virtual void Stop(string serviceName)
|
||||
|
||||
public ServiceControllerStatus GetStatus(string serviceName)
|
||||
{
|
||||
return GetService(serviceName).Status;
|
||||
return GetService(serviceName).Status;
|
||||
}
|
||||
|
||||
public void Start(string serviceName)
|
||||
|
@ -1,21 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNet4.SocksProxy" Version="1.4.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="NLog.Targets.Syslog" Version="6.0.3" />
|
||||
<PackageReference Include="Sentry" Version="1.2.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.8" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.0-0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="6.0.0-preview.5.21301.5" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="EnsureThat\Resources\ExceptionMessages.Designer.cs">
|
||||
|
@ -17,7 +17,7 @@ public Debouncer(Action action, TimeSpan debounceDuration)
|
||||
_timer.Elapsed += timer_Elapsed;
|
||||
}
|
||||
|
||||
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
if (_paused == 0)
|
||||
{
|
||||
|
@ -1,12 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
|
||||
<ApplicationIcon>..\NzbDrone.Host\Sonarr.ico</ApplicationIcon>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="!$(RuntimeIdentifier.StartsWith('win'))">
|
||||
<AssemblyName>Sonarr</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Host\Sonarr.Host.csproj" />
|
||||
<ProjectReference Include="..\Sonarr.RuntimePatches\Sonarr.RuntimePatches.csproj" />
|
||||
|
@ -164,7 +164,7 @@ public void Download_report_should_trigger_indexer_backoff_on_http429_based_on_d
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(),
|
||||
It.IsInRange<TimeSpan>(TimeSpan.FromMinutes(4.9), TimeSpan.FromMinutes(5.1), Range.Inclusive)), Times.Once());
|
||||
It.IsInRange<TimeSpan>(TimeSpan.FromMinutes(4.9), TimeSpan.FromMinutes(5.1), Moq.Range.Inclusive)), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class DotnetVersionCheckFixture : CoreTest<DotnetVersionCheck>
|
||||
{
|
||||
private void GivenOutput(string version)
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>()
|
||||
.SetupGet(s => s.Version)
|
||||
.Returns(new Version(version));
|
||||
}
|
||||
|
||||
[TestCase("4.7.2")]
|
||||
[TestCase("4.8")]
|
||||
public void should_return_ok(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
public void should_return_notice(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeNotice();
|
||||
}
|
||||
|
||||
public void should_return_warning(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[TestCase("4.5")]
|
||||
[TestCase("4.5.2")]
|
||||
[TestCase("4.6.1")]
|
||||
[TestCase("4.6.2")]
|
||||
[TestCase("4.7")]
|
||||
[TestCase("4.7.1")]
|
||||
public void should_return_error(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_nok_for_net462_on_Win1511()
|
||||
{
|
||||
Mocker.GetMock<IOsInfo>()
|
||||
.SetupGet(v => v.Version)
|
||||
.Returns("10.0.14392");
|
||||
|
||||
GivenOutput("4.6.2");
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
using static NzbDrone.Core.HealthCheck.Checks.MonoDebugCheck;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class MonoDebugFixture : CoreTest<MonoDebugCheck>
|
||||
{
|
||||
private void GivenHasStackFrame(bool hasStackFrame)
|
||||
{
|
||||
Mocker.GetMock<StackFrameHelper>()
|
||||
.Setup(f => f.HasStackFrameInfo())
|
||||
.Returns(hasStackFrame);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_not_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(false);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_warning_if_not_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(false);
|
||||
|
||||
Subject.Check();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_if_debug()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
GivenHasStackFrame(true);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class MonoVersionCheckFixture : CoreTest<MonoVersionCheck>
|
||||
{
|
||||
private void GivenOutput(string version)
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>()
|
||||
.SetupGet(s => s.Version)
|
||||
.Returns(new Version(version));
|
||||
}
|
||||
|
||||
[TestCase("5.18")]
|
||||
[TestCase("5.20")]
|
||||
[TestCase("6.4")]
|
||||
public void should_return_ok(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
public void should_return_notice(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeNotice();
|
||||
}
|
||||
|
||||
public void should_return_warning(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[TestCase("2.10.2")]
|
||||
[TestCase("2.10.8.1")]
|
||||
[TestCase("3.0.0.1")]
|
||||
[TestCase("3.2.0.1")]
|
||||
[TestCase("3.2.1")]
|
||||
[TestCase("3.2.7")]
|
||||
[TestCase("3.6.1")]
|
||||
[TestCase("3.8")]
|
||||
[TestCase("3.10")]
|
||||
[TestCase("4.0.0.0")]
|
||||
[TestCase("4.2")]
|
||||
[TestCase("4.4.0")]
|
||||
[TestCase("4.4.1")]
|
||||
[TestCase("5.4")]
|
||||
[TestCase("5.8")]
|
||||
[TestCase("5.10")]
|
||||
[TestCase("5.12")]
|
||||
[TestCase("5.14")]
|
||||
[TestCase("5.16")]
|
||||
public void should_return_error(string version)
|
||||
{
|
||||
GivenOutput(version);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using Moq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
@ -31,7 +31,7 @@ public void should_return_error_when_app_folder_is_write_protected()
|
||||
[Test]
|
||||
public void should_return_error_when_app_folder_is_write_protected_and_update_automatically_is_enabled()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
const string startupFolder = @"/opt/nzbdrone";
|
||||
|
||||
@ -53,7 +53,7 @@ public void should_return_error_when_app_folder_is_write_protected_and_update_au
|
||||
[Test]
|
||||
public void should_return_error_when_ui_folder_is_write_protected_and_update_automatically_is_enabled()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
const string startupFolder = @"/opt/nzbdrone";
|
||||
const string uiFolder = @"/opt/nzbdrone/UI";
|
||||
@ -80,7 +80,7 @@ public void should_return_error_when_ui_folder_is_write_protected_and_update_aut
|
||||
[Test]
|
||||
public void should_not_return_error_when_app_folder_is_write_protected_and_external_script_enabled()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
Mocker.GetMock<IConfigFileProvider>()
|
||||
.Setup(s => s.UpdateAutomatically)
|
||||
|
@ -302,7 +302,7 @@ public void should_get_relative_path_when_there_is_no_grandparent_windows()
|
||||
[Test]
|
||||
public void should_get_relative_path_when_there_is_no_grandparent_mono()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr";
|
||||
var outputPath = "/";
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@ -74,7 +74,7 @@ public void should_return_false_if_in_working_folder_and_last_write_time_was_rec
|
||||
[Test]
|
||||
public void should_return_false_if_unopacking_on_linux()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
GivenInWorkingFolder();
|
||||
GivenLastWriteTimeUtc(DateTime.UtcNow.AddDays(-5));
|
||||
|
@ -111,7 +111,7 @@ public void filter_should_return_none_existing_files_ignoring_case()
|
||||
[Test]
|
||||
public void filter_should_return_none_existing_files_not_ignoring_case()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
var files = new List<string>()
|
||||
{
|
||||
|
@ -1,10 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBuilder" Version="6.0.0" />
|
||||
<PackageReference Include="NBuilder" Version="6.1.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.0-0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -14,6 +14,11 @@ public class UpdatePackageProviderFixture : CoreTest<UpdatePackageProvider>
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
if (OsInfo.Os == Os.LinuxMusl || OsInfo.Os == Os.Bsd)
|
||||
{
|
||||
throw new IgnoreException("Ignore until we have musl releases");
|
||||
}
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>().SetupGet(c => c.Version).Returns(new Version("9.9.9"));
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
@ -50,10 +51,11 @@ public void should_not_be_valid_if_child_of_windows_folder()
|
||||
[Test]
|
||||
public void should_not_be_valid_if_set_to_bin_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
var bin = OsInfo.IsOsx ? "/System" : "/bin";
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = "/bin")
|
||||
.With(s => s.Path = bin)
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
@ -62,10 +64,11 @@ public void should_not_be_valid_if_set_to_bin_folder()
|
||||
[Test]
|
||||
public void should_not_be_valid_if_child_of_bin_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
PosixOnly();
|
||||
|
||||
var bin = OsInfo.IsOsx ? "/System" : "/bin";
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = "/bin/test")
|
||||
.With(s => s.Path = Path.Combine(bin, "test"))
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
|
@ -181,7 +181,7 @@ public AuthenticationType AuthenticationMethod
|
||||
|
||||
public string Branch => GetValue("Branch", "main").ToLowerInvariant();
|
||||
|
||||
public string LogLevel => GetValue("LogLevel", "info");
|
||||
public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
|
||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||
|
||||
public string SslCertPath => GetValue("SslCertPath", "");
|
||||
@ -202,8 +202,7 @@ public string UrlBase
|
||||
}
|
||||
}
|
||||
|
||||
// public string UiFolder => GetValue("UiFolder", "UI", false);GetValue("UiFolder", "UI", false);
|
||||
public string UiFolder => "UI";
|
||||
public string UiFolder => BuildInfo.IsDebug ? Path.Combine("..", "UI") : "UI";
|
||||
|
||||
public string InstanceName
|
||||
{
|
||||
@ -257,7 +256,7 @@ public string GetValue(string key, object defaultValue, bool persist = true)
|
||||
|
||||
var valueHolder = parentContainer.Descendants(key).ToList();
|
||||
|
||||
if (valueHolder.Count() == 1)
|
||||
if (valueHolder.Count == 1)
|
||||
{
|
||||
return valueHolder.First().Value.Trim();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
@ -12,7 +12,7 @@ public interface IHadoukenProxy
|
||||
{
|
||||
HadoukenSystemInfo GetSystemInfo(HadoukenSettings settings);
|
||||
HadoukenTorrent[] GetTorrents(HadoukenSettings settings);
|
||||
IDictionary<string, object> GetConfig(HadoukenSettings settings);
|
||||
IReadOnlyDictionary<string, object> GetConfig(HadoukenSettings settings);
|
||||
string AddTorrentFile(HadoukenSettings settings, byte[] fileContent);
|
||||
void AddTorrentUri(HadoukenSettings settings, string torrentUrl);
|
||||
void RemoveTorrent(HadoukenSettings settings, string downloadId);
|
||||
@ -42,9 +42,9 @@ public HadoukenTorrent[] GetTorrents(HadoukenSettings settings)
|
||||
return GetTorrents(result.Torrents);
|
||||
}
|
||||
|
||||
public IDictionary<string, object> GetConfig(HadoukenSettings settings)
|
||||
public IReadOnlyDictionary<string, object> GetConfig(HadoukenSettings settings)
|
||||
{
|
||||
return ProcessRequest<IDictionary<string, object>>(settings, "webui.getSettings");
|
||||
return ProcessRequest<IReadOnlyDictionary<string, object>>(settings, "webui.getSettings");
|
||||
}
|
||||
|
||||
public string AddTorrentFile(HadoukenSettings settings, byte[] fileContent)
|
||||
|
@ -1,4 +1,4 @@
|
||||
using FluentValidation.Results;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
@ -35,7 +35,7 @@ protected override OsPath GetOutputPath(OsPath outputPath, TransmissionTorrent t
|
||||
// - A multi-file torrent is downloaded in a job folder and 'outputPath' points to that directory directly.
|
||||
// - A single-file torrent is downloaded in the root folder and 'outputPath' poinst to that root folder.
|
||||
// We have to make sure the return value points to the job folder OR file.
|
||||
if (outputPath == null || outputPath.FileName == torrent.Name || torrent.FileCount > 1)
|
||||
if (outputPath.FileName == torrent.Name || torrent.FileCount > 1)
|
||||
{
|
||||
_logger.Trace("Vuze output directory: {0}", outputPath);
|
||||
}
|
||||
|
@ -313,11 +313,8 @@ public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile ep
|
||||
video.Add(new XElement("scantype", episodeFile.MediaInfo.ScanType));
|
||||
video.Add(new XElement("width", episodeFile.MediaInfo.Width));
|
||||
|
||||
if (episodeFile.MediaInfo.RunTime != null)
|
||||
{
|
||||
video.Add(new XElement("duration", episodeFile.MediaInfo.RunTime.TotalMinutes));
|
||||
video.Add(new XElement("durationinseconds", Math.Round(episodeFile.MediaInfo.RunTime.TotalSeconds)));
|
||||
}
|
||||
video.Add(new XElement("duration", episodeFile.MediaInfo.RunTime.TotalMinutes));
|
||||
video.Add(new XElement("durationinseconds", Math.Round(episodeFile.MediaInfo.RunTime.TotalSeconds)));
|
||||
|
||||
streamDetails.Add(video);
|
||||
|
||||
|
@ -8,7 +8,7 @@ public static class Hashing
|
||||
{
|
||||
public static string SHA256Hash(this string input)
|
||||
{
|
||||
using (var hash = SHA256Managed.Create())
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
var enc = Encoding.UTF8;
|
||||
return GetHash(hash.ComputeHash(enc.GetBytes(input)));
|
||||
@ -17,7 +17,7 @@ public static string SHA256Hash(this string input)
|
||||
|
||||
public static string SHA256Hash(this Stream input)
|
||||
{
|
||||
using (var hash = SHA256Managed.Create())
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
return GetHash(hash.ComputeHash(input));
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class DotnetVersionCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly IOsInfo _osInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DotnetVersionCheck(IPlatformInfo platformInfo, IOsInfo osInfo, Logger logger)
|
||||
{
|
||||
_platformInfo = platformInfo;
|
||||
_osInfo = osInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (!PlatformInfo.IsDotNet)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var dotnetVersion = _platformInfo.Version;
|
||||
|
||||
// Target .Net version, which would allow us to increase our target framework
|
||||
var targetVersion = new Version("4.7.2");
|
||||
if (dotnetVersion >= targetVersion)
|
||||
{
|
||||
_logger.Debug("Dotnet version is {0} or better: {1}", targetVersion, dotnetVersion);
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
// Supported .net version but below our desired target
|
||||
var stableVersion = new Version("4.7.2");
|
||||
if (dotnetVersion >= stableVersion)
|
||||
{
|
||||
_logger.Debug("Dotnet version is {0} or better: {1}", stableVersion, dotnetVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is supported but we recommend upgrading to at least {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-supported-but-upgrading-is-recommended");
|
||||
}
|
||||
|
||||
if (Version.TryParse(_osInfo.Version, out var osVersion) && osVersion < new Version("10.0.14393"))
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is no longer supported. However your Operating System cannot be upgraded to {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
var oldVersion = new Version("4.6.2");
|
||||
if (dotnetVersion >= oldVersion)
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is no longer supported. Please upgrade the .Net Framework to at least {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed .Net Framework {dotnetVersion} is old and unsupported. Please upgrade the .Net Framework to at least {targetVersion}.",
|
||||
"#currently-installed-net-framework-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class MonoDebugCheck : HealthCheckBase
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly StackFrameHelper _stackFrameHelper;
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
|
||||
public MonoDebugCheck(Logger logger, StackFrameHelper stackFrameHelper)
|
||||
{
|
||||
_logger = logger;
|
||||
_stackFrameHelper = stackFrameHelper;
|
||||
}
|
||||
|
||||
public class StackFrameHelper
|
||||
{
|
||||
public virtual bool HasStackFrameInfo()
|
||||
{
|
||||
var stackTrace = new StackTrace(true);
|
||||
|
||||
return stackTrace.FrameCount > 0 && stackTrace.GetFrame(0).GetFileName().IsNotNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (!PlatformInfo.IsMono)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
if (!_stackFrameHelper.HasStackFrameInfo())
|
||||
{
|
||||
_logger.Warn("Mono is not running with --debug switch");
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NLog;
|
||||
using NLog.Fluent;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class MonoTlsCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MonoTlsCheck(IPlatformInfo platformInfo, Logger logger)
|
||||
{
|
||||
_platformInfo = platformInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (!PlatformInfo.IsMono)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var monoVersion = _platformInfo.Version;
|
||||
|
||||
if (monoVersion >= new Version("5.8.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy")
|
||||
{
|
||||
_logger.Debug()
|
||||
.Message("Mono version {0} and legacy TLS provider is selected, recommending user to switch to btls.", monoVersion)
|
||||
.WriteSentryDebug("LegacyTlsProvider", monoVersion.ToString())
|
||||
.Write();
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Sonarr Mono 4.x tls workaround still enabled, consider removing MONO_TLS_PROVIDER=legacy environment option");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
public class MonoVersionCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IPlatformInfo _platformInfo;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MonoVersionCheck(IPlatformInfo platformInfo, Logger logger)
|
||||
{
|
||||
_platformInfo = platformInfo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
if (!PlatformInfo.IsMono)
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
var monoVersion = _platformInfo.Version;
|
||||
|
||||
// Known buggy Mono versions
|
||||
if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1"))
|
||||
{
|
||||
_logger.Debug("Mono version {0}", monoVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version",
|
||||
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
// Currently best stable Mono version (5.18 gets us .net 4.7.2 support)
|
||||
var bestVersion = new Version("5.20");
|
||||
var targetVersion = new Version("5.18");
|
||||
if (monoVersion >= targetVersion)
|
||||
{
|
||||
_logger.Debug("Mono version is {0} or better: {1}", targetVersion, monoVersion);
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
// Stable Mono versions
|
||||
var stableVersion = new Version("5.18");
|
||||
if (monoVersion >= stableVersion)
|
||||
{
|
||||
_logger.Debug("Mono version is {0} or better: {1}", stableVersion, monoVersion);
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||
$"Currently installed Mono version {monoVersion} is supported but upgrading to {bestVersion} is recommended.",
|
||||
"#currently-installed-mono-version-is-supported-but-upgrading-is-recommended");
|
||||
}
|
||||
|
||||
var oldVersion = new Version("5.4");
|
||||
if (monoVersion >= oldVersion)
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed Mono version {monoVersion} is no longer supported. Please upgrade Mono to version {bestVersion}.",
|
||||
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||
$"Currently installed Mono version {monoVersion} is old and unsupported. Please upgrade Mono to version {bestVersion}.",
|
||||
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||
}
|
||||
|
||||
public override bool CheckOnSchedule => false;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.UI.WebControls;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
|
@ -1,4 +1,4 @@
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
@ -21,12 +21,6 @@ public ImageResizer(IDiskProvider diskProvider, IPlatformInfo platformInfo)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
|
||||
// Random segfaults on mono 5.0 and 5.4
|
||||
if (PlatformInfo.IsMono && platformInfo.Version < new System.Version(5, 8))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_enabled = true;
|
||||
|
||||
// More conservative memory allocation
|
||||
|
@ -1,8 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.UI;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Profiles.Releases;
|
||||
|
@ -312,58 +312,58 @@ public int Count_Get(StreamKind streamKind, int streamNumber = -1)
|
||||
return (int)MediaInfo_Count_Get(_handle, (IntPtr)streamKind, (IntPtr)streamNumber);
|
||||
}
|
||||
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_New();
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern void MediaInfo_Delete(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Open(IntPtr handle, IntPtr fileName);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Open_Buffer_Init(IntPtr handle, long fileSize, long fileOffset);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern long MediaInfo_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Open_Buffer_Finalize(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern void MediaInfo_Close(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Option(IntPtr handle, IntPtr option, IntPtr value);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_State_Get(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
private static extern IntPtr MediaInfo_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber);
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfo_Count_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber);
|
||||
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_New();
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern void MediaInfoA_Delete(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Open(IntPtr handle, IntPtr fileName);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Open_Buffer_Init(IntPtr handle, long fileSize, long fileOffset);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Open_Buffer_Continue(IntPtr handle, byte[] buffer, IntPtr bufferSize);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern long MediaInfoA_Open_Buffer_Continue_GoTo_Get(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Open_Buffer_Finalize(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern void MediaInfoA_Close(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_GetI(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber, IntPtr parameter, IntPtr infoKind, IntPtr searchKind);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Option(IntPtr handle, IntPtr option, IntPtr value);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_State_Get(IntPtr handle);
|
||||
[DllImport("MediaInfo.dll")]
|
||||
private static extern IntPtr MediaInfoA_Count_Get(IntPtr handle, IntPtr StreamKind, IntPtr streamNumber);
|
||||
[DllImport("mediainfo")]
|
||||
private static extern IntPtr MediaInfoA_Count_Get(IntPtr handle, IntPtr streamKind, IntPtr streamNumber);
|
||||
}
|
||||
}
|
||||
|
@ -608,7 +608,7 @@ private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> token
|
||||
|
||||
private const string MediaInfoVideoDynamicRangeToken = "{MediaInfo VideoDynamicRange}";
|
||||
private const string MediaInfoVideoDynamicRangeTypeToken = "{MediaInfo VideoDynamicRangeType}";
|
||||
private static readonly IDictionary<string, int> MinimumMediaInfoSchemaRevisions =
|
||||
private static readonly IReadOnlyDictionary<string, int> MinimumMediaInfoSchemaRevisions =
|
||||
new Dictionary<string, int>(FileNameBuilderTokenEqualityComparer.Instance)
|
||||
{
|
||||
{MediaInfoVideoDynamicRangeToken, 5},
|
||||
|
@ -1,9 +0,0 @@
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Core.Rest
|
||||
{
|
||||
public interface IRestClientFactory
|
||||
{
|
||||
RestClient BuildClient(string baseUrl);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
using RestSharp.Serializers;
|
||||
|
||||
namespace NzbDrone.Core.Rest
|
||||
{
|
||||
public class JsonNetSerializer : ISerializer
|
||||
{
|
||||
public JsonNetSerializer()
|
||||
{
|
||||
ContentType = "application/json";
|
||||
}
|
||||
|
||||
public string Serialize(object obj)
|
||||
{
|
||||
return obj.ToJson();
|
||||
}
|
||||
|
||||
public string RootElement { get; set; }
|
||||
public string Namespace { get; set; }
|
||||
public string DateFormat { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
using RestSharp;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
namespace NzbDrone.Core.Rest
|
||||
{
|
||||
public class RestClientFactory : IRestClientFactory
|
||||
{
|
||||
private readonly IHttpProxySettingsProvider _httpProxySettingsProvider;
|
||||
private readonly ICreateManagedWebProxy _createManagedWebProxy;
|
||||
|
||||
public RestClientFactory(IHttpProxySettingsProvider httpProxySettingsProvider, ICreateManagedWebProxy createManagedWebProxy)
|
||||
{
|
||||
_httpProxySettingsProvider = httpProxySettingsProvider;
|
||||
_createManagedWebProxy = createManagedWebProxy;
|
||||
}
|
||||
|
||||
public RestClient BuildClient(string baseUrl)
|
||||
{
|
||||
var restClient = new RestClient(baseUrl)
|
||||
{
|
||||
UserAgent = $"{BuildInfo.AppName}/{BuildInfo.Version} ({OsInfo.Os})"
|
||||
};
|
||||
|
||||
var proxySettings = _httpProxySettingsProvider.GetProxySettings();
|
||||
if (proxySettings != null)
|
||||
{
|
||||
restClient.Proxy = _createManagedWebProxy.GetWebProxy(proxySettings);
|
||||
}
|
||||
|
||||
return restClient;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Core.Rest
|
||||
{
|
||||
public class RestException : Exception
|
||||
{
|
||||
public IRestResponse Response { get; private set; }
|
||||
|
||||
public RestException(IRestResponse response, IRestClient restClient)
|
||||
: base(string.Format("REST request failed: [{0}] [{1}] at [{2}]", (int)response.StatusCode, response.Request.Method, restClient.BuildUri(response.Request)))
|
||||
{
|
||||
Response = response;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Response != null)
|
||||
{
|
||||
return base.ToString() + Environment.NewLine + Response.Content;
|
||||
}
|
||||
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Core.Rest
|
||||
{
|
||||
public static class RestSharpExtensions
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(RestSharpExtensions));
|
||||
|
||||
public static IRestResponse ValidateResponse(this IRestResponse response, IRestClient restClient)
|
||||
{
|
||||
Ensure.That(response, () => response).IsNotNull();
|
||||
|
||||
if (response.Request == null && response.ErrorException != null)
|
||||
{
|
||||
throw response.ErrorException;
|
||||
}
|
||||
|
||||
Ensure.That(response.Request, () => response.Request).IsNotNull();
|
||||
Ensure.That(restClient, () => restClient).IsNotNull();
|
||||
|
||||
Logger.Debug("Validating Responses from [{0}] [{1}] status: [{2}]", response.Request.Method, restClient.BuildUri(response.Request), response.StatusCode);
|
||||
|
||||
if (response.ResponseUri == null)
|
||||
{
|
||||
Logger.Error(response.ErrorException, "Error communicating with server");
|
||||
throw response.ErrorException;
|
||||
}
|
||||
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
case HttpStatusCode.OK:
|
||||
{
|
||||
return response;
|
||||
}
|
||||
case HttpStatusCode.NoContent:
|
||||
{
|
||||
return response;
|
||||
}
|
||||
case HttpStatusCode.Created:
|
||||
{
|
||||
return response;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Logger.Warn("[{0}] [{1}] Failed. [{2}]", response.Request.Method, response.ResponseUri.ToString(), response.StatusCode);
|
||||
throw new RestException(response, restClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T Read<T>(this IRestResponse restResponse, IRestClient restClient) where T : class, new()
|
||||
{
|
||||
restResponse.ValidateResponse(restClient);
|
||||
|
||||
if (restResponse.Content != null)
|
||||
{
|
||||
Logger.Trace("Response: " + restResponse.Content);
|
||||
}
|
||||
|
||||
return Json.Deserialize<T>(restResponse.Content);
|
||||
}
|
||||
|
||||
public static T ExecuteAndValidate<T>(this IRestClient client, IRestRequest request) where T : class, new()
|
||||
{
|
||||
return client.Execute(request).Read<T>(client);
|
||||
}
|
||||
|
||||
public static IRestResponse ExecuteAndValidate(this IRestClient client, IRestRequest request)
|
||||
{
|
||||
return client.Execute(request).ValidateResponse(client);
|
||||
}
|
||||
|
||||
public static void AddQueryString(this IRestRequest request, string name, object value)
|
||||
{
|
||||
request.AddParameter(name, value.ToString(), ParameterType.GetOrPost);
|
||||
}
|
||||
|
||||
public static object GetHeaderValue(this IRestResponse response, string key)
|
||||
{
|
||||
var header = response.Headers.FirstOrDefault(v => v.Name == key);
|
||||
|
||||
if (header == null) return null;
|
||||
|
||||
return header.Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<Platforms>x86</Platforms>
|
||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentMigrator.Runner" Version="3.3.2" />
|
||||
@ -11,9 +10,8 @@
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0007" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
<PackageReference Include="MailKit" Version="2.10.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NLog" Version="4.7.14" />
|
||||
<PackageReference Include="RestSharp" Version="106.15.0" />
|
||||
<PackageReference Include="TinyTwitter" Version="1.1.2" />
|
||||
<PackageReference Include="Kveer.XmlRPC" Version="1.2.0" />
|
||||
<PackageReference Include="MonoTorrent" Version="2.0.5" />
|
||||
@ -23,17 +21,10 @@
|
||||
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj" />
|
||||
<ProjectReference Include="..\NzbDrone.Common\Sonarr.Common.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\..\Logo\64.png">
|
||||
<Link>Resources\Logo\64.png</Link>
|
||||
</EmbeddedResource>
|
||||
<None Include="..\Libraries\MediaInfo\MediaInfo.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Download\Clients\Aria2\" />
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user