From e329f34ff1004823311a74287d60db9858679925 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 21:49:17 -0800 Subject: [PATCH 01/16] refactor: build with cake frosting --- .gitattributes | 2 +- .gitignore | 31 +- AutoUpdate/CKAN-autoupdate.csproj | 2 +- Cmdline/CKAN-cmdline.csproj | 2 +- ConsoleUI/CKAN-ConsoleUI.csproj | 2 +- Core/CKAN-core.csproj | 2 +- GUI/CKAN-GUI.csproj | 2 +- Netkan/CKAN-netkan.csproj | 2 +- Tests/Tests.csproj | 2 +- build | 65 ---- build.cake | 524 ------------------------------ build.ps1 | 62 +--- build.sh | 3 + build/Build.csproj | 15 + build/BuildContext.cs | 149 +++++++++ build/BuildLifetime.cs | 41 +++ build/BuildPaths.cs | 43 +++ build/MakeTasks.cs | 72 ++++ build/Program.cs | 372 +++++++++++++++++++++ cake.config | 4 - 20 files changed, 735 insertions(+), 662 deletions(-) delete mode 100755 build delete mode 100644 build.cake create mode 100755 build.sh create mode 100644 build/Build.csproj create mode 100644 build/BuildContext.cs create mode 100644 build/BuildLifetime.cs create mode 100644 build/BuildPaths.cs create mode 100644 build/MakeTasks.cs create mode 100644 build/Program.cs delete mode 100644 cake.config diff --git a/.gitattributes b/.gitattributes index 833392b63c..98bb8e21db 100644 --- a/.gitattributes +++ b/.gitattributes @@ -40,7 +40,7 @@ # Build files *.cake text *.ps1 text -build text eol=lf +*.sh text eol=lf Dockerfile text eol=lf # Other diff --git a/.gitignore b/.gitignore index 3e7b8a28d7..54f7fcdccb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /_build/ /.vs/ -/tools +/build/tools test-results *.userprefs *.csproj.user @@ -52,3 +52,32 @@ bld/ /.venv/ quotes.txt.dat + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + diff --git a/AutoUpdate/CKAN-autoupdate.csproj b/AutoUpdate/CKAN-autoupdate.csproj index 68385ade6b..944d7b6d15 100644 --- a/AutoUpdate/CKAN-autoupdate.csproj +++ b/AutoUpdate/CKAN-autoupdate.csproj @@ -65,7 +65,7 @@ - diff --git a/Cmdline/CKAN-cmdline.csproj b/Cmdline/CKAN-cmdline.csproj index dbec83401b..231b4b2910 100644 --- a/Cmdline/CKAN-cmdline.csproj +++ b/Cmdline/CKAN-cmdline.csproj @@ -93,7 +93,7 @@ - diff --git a/ConsoleUI/CKAN-ConsoleUI.csproj b/ConsoleUI/CKAN-ConsoleUI.csproj index 56918eb4fc..afbeca5630 100644 --- a/ConsoleUI/CKAN-ConsoleUI.csproj +++ b/ConsoleUI/CKAN-ConsoleUI.csproj @@ -83,7 +83,7 @@ - diff --git a/Core/CKAN-core.csproj b/Core/CKAN-core.csproj index ff516cd54d..574815698b 100644 --- a/Core/CKAN-core.csproj +++ b/Core/CKAN-core.csproj @@ -101,7 +101,7 @@ - diff --git a/GUI/CKAN-GUI.csproj b/GUI/CKAN-GUI.csproj index aed179e0ad..f0092df8ee 100644 --- a/GUI/CKAN-GUI.csproj +++ b/GUI/CKAN-GUI.csproj @@ -111,7 +111,7 @@ - diff --git a/Netkan/CKAN-netkan.csproj b/Netkan/CKAN-netkan.csproj index ef0a2801aa..5a14b33450 100644 --- a/Netkan/CKAN-netkan.csproj +++ b/Netkan/CKAN-netkan.csproj @@ -71,7 +71,7 @@ - diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 8ed9286771..34687a8352 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -97,7 +97,7 @@ - diff --git a/build b/build deleted file mode 100755 index 71aec17d04..0000000000 --- a/build +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -set -e - -arg0="" -remainingArgs="" - -if [ $# -gt 0 ]; then - arg0="$1" -fi - -if [ $# -gt 1 ]; then - skippedFirst=false - for i in "$@"; do - if $skippedFirst; then - remainingArgs="$remainingArgs $i" - else - skippedFirst=true - fi - done -fi - -nugetVersion="6.8.0" -useExperimental=false -rootDir=$(dirname $0) -scriptFile="$rootDir/build.cake" -buildDir="$rootDir/_build" -toolsDir="$buildDir/tools" -nugetExe="$toolsDir/NuGet/$nugetVersion/nuget.exe" - -nugetDir="$(dirname $nugetExe)" -if [ ! -d "$nugetDir" ]; then - mkdir -p "$nugetDir" -fi - -if [ ! -f "$nugetExe" ]; then - curl -L "https://dist.nuget.org/win-x86-commandline/v${nugetVersion}/nuget.exe" --output "$nugetExe" -fi - -# dotnet tool install idiotically returns failure if already installed, -# so we have to ignore all errors from it -# https://github.com/dotnet/sdk/issues/9500 -set +e -dotnet tool install --global Cake.Tool -set -e - -cakeArgs="" - -if [ -n "$arg0" ]; then - case $arg0 in - -*) - cakeArgs="$cakeArgs $arg0" - ;; - *) - cakeArgs="$cakeArgs --target=$arg0" - ;; - esac -fi - -if $useExperimental; then - cakeArgs="$cakeArgs --experimental" -fi - -export PATH="$PATH:$HOME/.dotnet/tools" -dotnet cake --verbosity Minimal "$scriptFile" $cakeArgs $remainingArgs -exit $? diff --git a/build.cake b/build.cake deleted file mode 100644 index f935a96b5e..0000000000 --- a/build.cake +++ /dev/null @@ -1,524 +0,0 @@ -#addin "nuget:?package=Cake.SemVer&version=4.0.0" -#addin "nuget:?package=semver&version=2.3.0" -#addin nuget:?package=Cake.Git&version=3.0.0 -#tool "nuget:?package=ILRepack&version=2.0.27" -#tool "nuget:?package=NUnit.ConsoleRunner&version=3.16.3" - -using System.Text.RegularExpressions; -using Semver; - -var buildNetFramework = "net48"; - -var target = Argument("target", "Default"); -var configuration = Argument("configuration", "Debug"); -var solution = Argument("solution", "CKAN.sln"); - -var rootDirectory = Context.Environment.WorkingDirectory; -var coreProj = rootDirectory.Combine("Core") - .CombineWithFilePath("CKAN-core.csproj") - .FullPath; -var buildDirectory = rootDirectory.Combine("_build"); -var nugetDirectory = buildDirectory.Combine("lib") - .Combine("nuget"); -var outDirectory = buildDirectory.Combine("out"); -var nupkgFile = outDirectory.Combine("CKAN") - .Combine(configuration) - .Combine("bin") - .CombineWithFilePath($"CKAN.{GetVersion(false)}.nupkg"); -var repackDirectory = buildDirectory.Combine("repack"); -var ckanFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("ckan.exe"); -var updaterFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("AutoUpdater.exe"); -var netkanFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("netkan.exe"); - -Task("Default") - .Description("Build ckan.exe and netkan.exe") - .IsDependentOn("Ckan") - .IsDependentOn("Netkan"); - -Task("Debug") - .Description("Build ckan.exe and netkan.exe in Debug configuration") - .IsDependentOn("Default"); - -Task("Release") - .Description("Build ckan.exe and netkan.exe in Release configuration") - .IsDependentOn("Default"); - -Task("Ckan") - .Description("Build only ckan.exe") - .IsDependentOn("Repack-Ckan"); - -Task("Netkan") - .Description("Build only netkan.exe") - .IsDependentOn("Repack-Netkan"); - -Task("osx") - .Description("Build the macOS(OSX) dmg package.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("macosx")); - -Task("osx-clean") - .Description("Clean the output directory of the macOS(OSX) package.") - .Does(() => MakeIn("macosx", "clean")); - -Task("deb") - .Description("Build the deb package for Debian-based distros.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("debian")); - -Task("deb-sign") - .Description("Build the deb package for Debian-based distros.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("debian", "sign")); - -Task("deb-test") - .Description("Test the deb packaging.") - .IsDependentOn("deb") - .Does(() => MakeIn("debian", "test")); - -Task("deb-clean") - .Description("Clean the deb output directory.") - .Does(() => MakeIn("debian", "clean")); - -Task("rpm") - .Description("Build the rpm package for RPM-based distros.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("rpm")); - -Task("rpm-repo") - .Description("Build the rpm repository for RPM-based distros.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("rpm", "repo")); - -Task("rpm-test") - .Description("Test the rpm packaging.") - .IsDependentOn("Ckan") - .Does(() => MakeIn("rpm", "test")); - -Task("rpm-clean") - .Description("Clean the rpm package output directory.") - .Does(() => MakeIn("rpm", "clean")); - -private void MakeIn(string dir, string args = null) -{ - int exitCode = StartProcess("make", new ProcessSettings { - WorkingDirectory = dir, - Arguments = args, - EnvironmentVariables = new Dictionary { { "CONFIGURATION", configuration } } - }); - if (exitCode != 0) - { - throw new Exception("Make failed with exit code: " + exitCode); - } -} - -Task("Restore") - .Description("Intermediate - Download dependencies") - .Does(() => - { - DotNetRestore(solution, new DotNetRestoreSettings - { - PackagesDirectory = nugetDirectory, - EnvironmentVariables = new Dictionary { { "Configuration", configuration } }, - }); - }); - -Task("Build") - .Description("Intermediate - Build everything") - .IsDependentOn("Restore") - .IsDependentOn("Generate-GlobalAssemblyVersionInfo") - .Does(() => - { - // dotnet build won't let us compile WinForms on non-Windows, - // fall back to mono - if (IsRunningOnWindows()) - { - DotNetBuild(solution, new DotNetBuildSettings - { - Configuration = configuration, - NoRestore = true, - }); - } - else - { - // Use dotnet to build the Core DLL to get the nupkg - // (only created if all TargetFrameworks are built together) - DotNetBuild(coreProj, new DotNetBuildSettings - { - Configuration = configuration, - }); - // Use Mono to build for net48 since dotnet can't use WinForms on Linux - MSBuild(solution, - settings => settings.SetConfiguration(configuration) - .SetMaxCpuCount(0) - .WithProperty("TargetFramework", - buildNetFramework)); - // Use dotnet to build the stuff Mono can't build - DotNetBuild(solution, new DotNetBuildSettings - { - Configuration = "NoGUI", - Framework = "net8.0", - }); - } - }); - -Task("Generate-GlobalAssemblyVersionInfo") - .Description("Intermediate - Calculate the version strings for the assembly.") - .Does(() => -{ - var metaDirectory = buildDirectory.Combine("meta"); - CreateDirectory(metaDirectory); - - var version = GetVersion(); - - CreateAssemblyInfo(metaDirectory.CombineWithFilePath("GlobalAssemblyVersionInfo.cs"), new AssemblyInfoSettings - { - Version = string.Format("{0}.{1}", version.Major, version.Minor), - FileVersion = string.IsNullOrEmpty(version.Metadata) - ? string.Format("{0}.{1}.{2}", - version.Major, version.Minor, version.Patch) - : string.Format("{0}.{1}.{2}.{3}", - version.Major, version.Minor, version.Patch, - version.Metadata), - InformationalVersion = version.ToString() - }); -}); - -Task("Repack-Ckan") - .Description("Intermediate - Merge all the separate DLLs and EXEs to a single executable.") - .IsDependentOn("Build") - .Does(() => -{ - CreateDirectory(repackDirectory.Combine(configuration)); - var cmdLineBinDirectory = outDirectory.Combine("CKAN-CmdLine") - .Combine(configuration) - .Combine("bin") - .Combine(buildNetFramework); - var assemblyPaths = GetFiles(string.Format("{0}/*.dll", cmdLineBinDirectory)); - assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-GUI.exe")); - assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-ConsoleUI.exe")); - assemblyPaths.Add(GetFiles(string.Format( - "{0}/*/*.resources.dll", - outDirectory.Combine("CKAN-CmdLine") - .Combine(configuration) - .Combine("bin") - .Combine(buildNetFramework)))); - // Need facade to instantiate types from netstandard2.0 DLLs on Mono - assemblyPaths.Add(FacadesDirectory().CombineWithFilePath("netstandard.dll")); - var ckanLogFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("ckan.log"); - ReportRepacking(ckanFile, ckanLogFile); - ILRepack( - ckanFile, - cmdLineBinDirectory.CombineWithFilePath("CKAN-CmdLine.exe"), - assemblyPaths, - new ILRepackSettings - { - Libs = new List { cmdLineBinDirectory }, - TargetPlatform = TargetPlatformVersion.v4, - Parallel = true, - Verbose = false, - SetupProcessSettings = RepackSilently, - Log = ckanLogFile.FullPath, - }); - - var autoupdateBinDirectory = outDirectory.Combine("CKAN-AutoUpdateHelper") - .Combine(configuration) - .Combine("bin") - .Combine(buildNetFramework); - var updaterLogFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("AutoUpdater.log"); - ReportRepacking(updaterFile, updaterLogFile); - ILRepack( - updaterFile, - autoupdateBinDirectory.CombineWithFilePath("CKAN-AutoUpdateHelper.exe"), - GetFiles(string.Format("{0}/*/*.resources.dll", - autoupdateBinDirectory)), - new ILRepackSettings - { - Libs = new List { autoupdateBinDirectory }, - TargetPlatform = TargetPlatformVersion.v4, - Parallel = true, - Verbose = false, - SetupProcessSettings = RepackSilently, - Log = updaterLogFile.FullPath, - }); - - CopyFile(ckanFile, buildDirectory.CombineWithFilePath(ckanFile.GetFilename())); -}); - -Task("Repack-Netkan") - .Description("Intermediate - Merge all the separate DLLs and EXEs to a single executable.") - .IsDependentOn("Build") - .Does(() => -{ - CreateDirectory(repackDirectory.Combine(configuration)); - var netkanBinDirectory = outDirectory.Combine("CKAN-NetKAN") - .Combine(configuration) - .Combine("bin") - .Combine(buildNetFramework); - var netkanLogFile = repackDirectory.Combine(configuration) - .CombineWithFilePath("netkan.log"); - var assemblyPaths = GetFiles(string.Format("{0}/*.dll", netkanBinDirectory)); - // Need facade to instantiate types from netstandard2.0 DLLs on Mono - assemblyPaths.Add(FacadesDirectory().CombineWithFilePath("netstandard.dll")); - ReportRepacking(netkanFile, netkanLogFile); - ILRepack( - netkanFile, - netkanBinDirectory.CombineWithFilePath("CKAN-NetKAN.exe"), - assemblyPaths, - new ILRepackSettings - { - Libs = new List { netkanBinDirectory }, - TargetPlatform = TargetPlatformVersion.v4, - Parallel = true, - Verbose = false, - SetupProcessSettings = RepackSilently, - Log = netkanLogFile.FullPath, - } - ); - - CopyFile(netkanFile, buildDirectory.CombineWithFilePath(netkanFile.GetFilename())); -}); - -Task("Prepare-SignPath") - .Description("Create a folder with all artifacts to be signed") - .IsDependentOn("Repack-Ckan") - .Does(() => -{ - var targetDir = buildDirectory.Combine("signpath") - .Combine(configuration); - CreateDirectory(targetDir); - CopyFile(ckanFile, targetDir.CombineWithFilePath(ckanFile.GetFilename())); - CopyFile(updaterFile, targetDir.CombineWithFilePath(updaterFile.GetFilename())); - CopyFile(nupkgFile, targetDir.CombineWithFilePath(nupkgFile.GetFilename())); -}); - -private void ReportRepacking(FilePath target, FilePath log) -{ - // ILRepack is extremly noisy by default and has no options to - // make it quieter other than shutting it up completely. - // - using (NormalVerbosity()) - { - Information("Repacking {0}, logging details to {1}...", - rootDirectory.GetRelativePath(target), - rootDirectory.GetRelativePath(log)); - } -} - -private void RepackSilently(ProcessSettings settings) - => settings.SetRedirectStandardOutput(true) - .SetRedirectedStandardOutputHandler(s => "") - .SetRedirectStandardError(true) - .SetRedirectedStandardErrorHandler(s => ""); - -Task("Test") - .Description("Run tests after compilation.") - .IsDependentOn("Default") - .IsDependentOn("Test+Only"); - -Task("Test+Only") - .Description("Run tests without compiling.") - .IsDependentOn("Test-Executables+Only") - .IsDependentOn("Test-UnitTests+Only"); - -Task("Test-Executables+Only") - .Description("Intermediate - Test executables without compiling.") - .IsDependentOn("Test-CkanExecutable+Only") - .IsDependentOn("Test-NetkanExecutable+Only"); - -Task("Test-CkanExecutable+Only") - .Description("Intermediate - Test ckan.exe without compiling.") - .Does(() => -{ - var output = RunExecutable(ckanFile, "version").FirstOrDefault(); - if (output != string.Format("v{0}", GetVersion())) - { - throw new Exception($"ckan.exe smoke test failed: {output}"); - } -}); - -Task("Test-NetkanExecutable+Only") - .Description("Intermediate - Test netkan.exe without compiling.") - .Does(() => -{ - var output = RunExecutable(netkanFile, "--version").FirstOrDefault(); - if (output != string.Format("v{0}", GetVersion())) - { - throw new Exception($"netkan.exe smoke test failed: {output}"); - } -}); - -Task("Test-UnitTests+Only") - .Description("Intermediate - Run unit tests without compiling.") - .Does(() => -{ - var where = Argument("where", null); - var nunitOutputDirectory = buildDirectory.Combine("test") - .Combine("nunit"); - CreateDirectory(nunitOutputDirectory); - // Only Mono's msbuild can handle WinForms on Linux, - // but dotnet build can handle multi-targeting on Windows - if (IsRunningOnWindows()) - { - DotNetTest(solution, new DotNetTestSettings - { - Configuration = configuration, - NoRestore = true, - NoBuild = true, - NoLogo = true, - Filter = where, - ResultsDirectory = nunitOutputDirectory, - Verbosity = DotNetVerbosity.Minimal, - }); - } - else - { - DotNetTest(solution, new DotNetTestSettings - { - Configuration = "NoGUI", - Framework = "net8.0", - NoRestore = true, - NoBuild = true, - NoLogo = true, - Filter = where, - ResultsDirectory = nunitOutputDirectory, - Verbosity = DotNetVerbosity.Minimal, - }); - var testFile = outDirectory.Combine("CKAN.Tests") - .Combine(configuration) - .Combine("bin") - .Combine(buildNetFramework) - .CombineWithFilePath("CKAN.Tests.dll"); - NUnit3(testFile.FullPath, new NUnit3Settings - { - Configuration = configuration, - Where = where, - Work = nunitOutputDirectory, - NoHeader = true, - // Work around System.Runtime.Remoting.RemotingException : Tcp transport error. - Process = NUnit3ProcessOption.InProcess, - }); - } -}); - -Task("Version") - .Description("Print the current CKAN version.") - .Does(() => -{ - using (NormalVerbosity()) - { - Information(GetVersion().ToString()); - } -}); - -Setup(context => -{ - var argConfiguration = Argument("configuration", null); - - if (string.Equals(target, "Release", StringComparison.OrdinalIgnoreCase)) - { - if (argConfiguration != null) - Warning($"Ignoring configuration argument: '{argConfiguration}'"); - - configuration = "Release"; - } - else if (string.Equals(target, "Debug", StringComparison.OrdinalIgnoreCase)) - { - if (argConfiguration != null) - Warning($"Ignoring configuration argument: '{argConfiguration}'"); - - configuration = "Debug"; - } -}); - -Teardown(context => -{ - var quote = GetQuote(rootDirectory.CombineWithFilePath("quotes.txt")); - - if (quote != null) - { - using (NormalVerbosity()) - { - Information(quote); - } - } -}); - -RunTarget(target); - -private DirectoryPath FacadesDirectory() - => IsRunningOnWindows() - ? Context.Environment.GetSpecialPath(SpecialPath.ProgramFilesX86) - .Combine("Reference Assemblies") - .Combine("Microsoft") - .Combine("Framework") - .Combine(".NETFramework") - .Combine("v4.8") - .Combine("Facades") - : new DirectoryPath("/usr").Combine("lib") - .Combine("mono") - .Combine("4.8-api") - .Combine("Facades"); - -private Semver.SemVersion GetVersion(bool withBuild = true) -{ - var pattern = new Regex(@"^\s*##\s+v(?\S+)\s?.*$"); - var rootDirectory = Context.Environment.WorkingDirectory; - - var versionMatch = System.IO.File - .ReadAllLines(rootDirectory.CombineWithFilePath("CHANGELOG.md").FullPath) - .Select(i => pattern.Match(i)) - .FirstOrDefault(i => i.Success); - - var version = ParseSemVer(versionMatch.Groups["version"].Value); - - if (withBuild && DirectoryExists(rootDirectory.Combine(".git"))) - { - var commitDate = GitLogTip(rootDirectory).Committer.When; - version = CreateSemVer(version.Major, - version.Minor, - version.Patch, - version.Prerelease, - commitDate.ToString("yy") + commitDate.DayOfYear.ToString("000")); - } - - return version; -} - -private IEnumerable RunExecutable(FilePath executable, string arguments) -{ - IEnumerable output; - var exitCode = StartProcess( - executable, - new ProcessSettings { Arguments = arguments, RedirectStandardOutput = true }, - out output - ); - - if (exitCode == 0) - { - return output; - } - else - { - throw new Exception("Process failed with exit code: " + exitCode); - } -} - -private string GetQuote(FilePath file) -{ - if (FileExists(file)) - { - var quotes = System.IO.File - .ReadAllText(file.FullPath) - .Split(new [] { '%' }, StringSplitOptions.RemoveEmptyEntries); - - if (quotes.Length > 0) - return quotes[(new Random()).Next(0, quotes.Length)]; - } - - return null; -} diff --git a/build.ps1 b/build.ps1 index 22c19dca6a..a7077190ae 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,60 +1,2 @@ -Param ( - [Parameter(Position = 0)] - [string]$Arg0, - - [Parameter(ValueFromRemainingArguments = $true)] - [Object[]]$RemainingArgs -) - -# PSScriptRoot isn't set in PowerShell 2 -$minPSVer = [version]"3.0" -if (($PSVersionTable.PSVersion -lt $minPSVer)) { - [Console]::ForegroundColor = 'red' - [Console]::Error.WriteLine("This script does not support PowerShell $($PSVersionTable.PSVersion).") - [Console]::Error.WriteLine("Please upgrade to PowerShell $minPSVer or later.") - [Console]::ResetColor() - exit -} - -# Globals -$NugetVersion = "6.8.0" -$UseExperimental = $false -$RootDir = "${PSScriptRoot}" -$ScriptFile = "${RootDir}/build.cake" -$BuildDir = "${RootDir}/_build" -$ToolsDir = "${BuildDir}/tools" -$NugetExe = "${ToolsDir}/NuGet/${NugetVersion}/nuget.exe" - -# Download NuGet -$NugetDir = Split-Path "$NugetExe" -Parent -if (!(Test-Path "$NugetDir")) { - mkdir $nugetDir > $null -} - -if (!(Test-Path "$NugetExe")) { - # Enable TLS1.2 for WebClient - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13 - (New-Object System.Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/v${NugetVersion}/nuget.exe", $NugetExe) -} - -# Install build packages -dotnet tool install --global Cake.Tool - -# Build args -$cakeArgs = @() - -if ($Arg0) { - if ($Arg0[0] -eq "-") { - $cakeArgs += "${Arg0}" - } else { - $cakeArgs += "--target=${Arg0}" - } -} - -if ($UseExperimental) { - $cakeArgs += "--experimental" -} - -# Run Cake -dotnet cake --verbosity Minimal "${ScriptFile}" ${cakeArgs} ${RemainingArgs} -exit $LASTEXITCODE +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..b4782143f3 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +dotnet run --project ./build/Build.csproj -- "$@" diff --git a/build/Build.csproj b/build/Build.csproj new file mode 100644 index 0000000000..c2e3091dfb --- /dev/null +++ b/build/Build.csproj @@ -0,0 +1,15 @@ + + + Exe + net9.0 + $(MSBuildProjectDirectory) + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/build/BuildContext.cs b/build/BuildContext.cs new file mode 100644 index 0000000000..962f1d63ec --- /dev/null +++ b/build/BuildContext.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Cake.Common; +using Cake.Common.Diagnostics; +using Cake.Common.IO; +using Cake.Core; +using Cake.Core.IO; +using Cake.Frosting; +using Cake.Git; + +namespace Build; + +public partial class BuildContext : FrostingContext +{ + public string BuildNetFramework { get; } = "net48"; + + public string Target { get; } + + // Named to avoid conflict with ICakeContext.Configuration + public string BuildConfiguration { get; set; } + public string Solution { get; } + + public BuildPaths Paths { get; } + + public BuildContext(ICakeContext context) + : base(context) + { + var rootDir = context.Environment.WorkingDirectory.GetParent(); + + Target = context.Argument("target", "Default"); + BuildConfiguration = context.Argument("configuration", "Debug"); + Solution = context.Argument("solution", rootDir.CombineWithFilePath("CKAN.sln").FullPath); + + Paths = new BuildPaths(rootDir, BuildConfiguration, GetVersion(false)); + } + + public SemVersion GetVersion(bool withBuild = true) + { + var rootDirectory = Environment.WorkingDirectory.GetParent(); + + var versionMatch = System.IO.File + .ReadAllLines(rootDirectory.CombineWithFilePath("CHANGELOG.md").FullPath) + .Select(i => VersionRegex().Match(i)) + .FirstOrDefault(i => i.Success); + + if (!SemVersion.TryParse(versionMatch.Groups["version"].Value, out var version)) + { + throw new System.Exception("Could not parse version from CHANGELOG.md"); + } + + if (withBuild && this.DirectoryExists(rootDirectory.Combine(".git"))) + { + var commitDate = this.GitLogTip(rootDirectory).Committer.When; + version = new SemVersion(version.Major, + version.Minor, + version.Patch, + version.PreRelease, + commitDate.ToString("yy") + commitDate.DayOfYear.ToString("000")); + } + + return version; + } + + [GeneratedRegex(@"^\s*##\s+v(?\S+)\s?.*$")] + private static partial Regex VersionRegex(); + + public DirectoryPath FacadesDirectory() + { + if (this.IsRunningOnWindows()) + { + return Environment.GetSpecialPath(SpecialPath.ProgramFilesX86) + .Combine("Reference Assemblies") + .Combine("Microsoft") + .Combine("Framework") + .Combine(".NETFramework") + .Combine("v4.8") + .Combine("Facades"); + } + + DirectoryPath monoLib; + if (this.IsRunningOnMacOs()) + { + monoLib = new DirectoryPath("/Library") + .Combine("Frameworks") + .Combine("Mono.framework") + .Combine("Versions") + .Combine("Current") + .Combine("lib"); + } + else + { + monoLib = new DirectoryPath("/usr").Combine("lib"); + } + + return monoLib + .Combine("mono") + .Combine("4.8-api") + .Combine("Facades"); + } + + public void ReportRepacking(FilePath target, FilePath log) + { + // ILRepack is extremely noisy by default and has no options to + // make it quieter other than shutting it up completely. + // + using (this.NormalVerbosity()) + { + this.Information("Repacking {0}, logging details to {1}...", + Paths.RootDirectory.GetRelativePath(target), + Paths.RootDirectory.GetRelativePath(log)); + } + } + + public static void RepackSilently(ProcessSettings settings) + => settings.SetRedirectStandardOutput(true) + .SetRedirectedStandardOutputHandler(s => "") + .SetRedirectStandardError(true) + .SetRedirectedStandardErrorHandler(s => ""); + + public string GetQuote(FilePath file) + { + if (!this.FileExists(file)) return null; + + var quotes = System.IO.File + .ReadAllText(file.FullPath) + .Split(['%'], StringSplitOptions.RemoveEmptyEntries); + + return quotes.Length > 0 ? quotes[new Random().Next(quotes.Length)] : null; + } + + public IEnumerable RunExecutable(FilePath executable, string arguments) + { + IEnumerable output; + var exitCode = this.StartProcess( + executable, + new ProcessSettings { Arguments = arguments, RedirectStandardOutput = true }, + out output + ); + + if (exitCode != 0) + { + throw new Exception("Process failed with exit code: " + exitCode); + } + + return output; + } +} \ No newline at end of file diff --git a/build/BuildLifetime.cs b/build/BuildLifetime.cs new file mode 100644 index 0000000000..59362a498b --- /dev/null +++ b/build/BuildLifetime.cs @@ -0,0 +1,41 @@ +using System; +using Cake.Common; +using Cake.Common.Diagnostics; +using Cake.Core; +using Cake.Frosting; + +namespace Build; + +public class BuildLifetime : FrostingLifetime +{ + public override void Setup(BuildContext context, ISetupContext info) + { + var argConfiguration = context.Argument("configuration", null); + + if (string.Equals(context.Target, "Release", StringComparison.OrdinalIgnoreCase)) + { + if (argConfiguration != null) + context.Warning($"Ignoring configuration argument: '{argConfiguration}'"); + + context.BuildConfiguration = "Release"; + } + else if (string.Equals(context.Target, "Debug", StringComparison.OrdinalIgnoreCase)) + { + if (argConfiguration != null) + context.Warning($"Ignoring configuration argument: '{argConfiguration}'"); + + context.BuildConfiguration = "Debug"; + } + } + + public override void Teardown(BuildContext context, ITeardownContext info) + { + var quote = context.GetQuote(context.Paths.RootDirectory.CombineWithFilePath("quotes.txt")); + if (quote == null) return; + + using (context.NormalVerbosity()) + { + context.Information(quote); + } + } +} \ No newline at end of file diff --git a/build/BuildPaths.cs b/build/BuildPaths.cs new file mode 100644 index 0000000000..6cda99aa82 --- /dev/null +++ b/build/BuildPaths.cs @@ -0,0 +1,43 @@ +using Cake.Common; +using Cake.Core.IO; + +namespace Build; + +public class BuildPaths +{ + public DirectoryPath RootDirectory { get; init; } + public FilePath CoreProject { get; } + public DirectoryPath BuildDirectory { get; } + public DirectoryPath NugetDirectory { get; } + public DirectoryPath OutDirectory { get; } + public FilePath NupkgFile { get; } + public DirectoryPath RepackDirectory { get; } + public FilePath CkanFile { get; } + public FilePath UpdaterFile { get; } + public FilePath NetkanFile { get; } + + public BuildPaths(DirectoryPath rootDirectory, string configuration, SemVersion version) + { + RootDirectory = rootDirectory; + CoreProject = rootDirectory.Combine("Core") + .CombineWithFilePath("CKAN-core.csproj"); + BuildDirectory = rootDirectory.Combine("_build"); + NugetDirectory = BuildDirectory.Combine("lib").Combine("nuget"); + OutDirectory = BuildDirectory.Combine("out"); + NupkgFile = OutDirectory + .Combine("CKAN") + .Combine(configuration) + .Combine("bin") + .CombineWithFilePath($"CKAN.{version}.nupkg"); + RepackDirectory = BuildDirectory.Combine("repack"); + CkanFile = RepackDirectory + .Combine(configuration) + .CombineWithFilePath("ckan.exe"); + UpdaterFile = RepackDirectory + .Combine(configuration) + .CombineWithFilePath("AutoUpdater.exe"); + NetkanFile = RepackDirectory + .Combine(configuration) + .CombineWithFilePath("netkan.exe"); + } +} \ No newline at end of file diff --git a/build/MakeTasks.cs b/build/MakeTasks.cs new file mode 100644 index 0000000000..f5a0549132 --- /dev/null +++ b/build/MakeTasks.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using Cake.Common; +using Cake.Core.IO; +using Cake.Frosting; + +namespace Build; + +[TaskName("osx")] +[TaskDescription("Build the macOS(OSX) dmg package.")] +[IsDependentOn(typeof(CkanTask))] +public sealed class OsxTask() : MakeTask("macosx"); + +[TaskName("osx-clean")] +[TaskDescription("Clean the output directory of the macOS(OSX) package.")] +public sealed class OsxCleanTask() : MakeTask("macosx", "clean"); + +[TaskName("deb")] +[TaskDescription("Build the deb package for Debian-based distros.")] +[IsDependentOn(typeof(CkanTask))] +public sealed class DebTask() : MakeTask("debian"); + +[TaskName("deb-sign")] +[TaskDescription("Build the deb package for Debian-based distros.")] +[IsDependentOn(typeof(DebTask))] +public sealed class DebSignTask() : MakeTask("debian", "sign"); + +[TaskName("deb-test")] +[TaskDescription("Test the deb packaging.")] +[IsDependentOn(typeof(DebTask))] +public sealed class DebTestTask() : MakeTask("debian", "test"); + +[TaskName("deb-clean")] +[TaskDescription("Clean the deb output directory.")] +public sealed class DebCleanTask() : MakeTask("debian", "clean"); + +[TaskName("rpm")] +[TaskDescription("Build the rpm package for RPM-based distros.")] +[IsDependentOn(typeof(CkanTask))] +public sealed class RpmTask() : MakeTask("rpm"); + +[TaskName("rpm-repo")] +[TaskDescription("Build the rpm repository for RPM-based distros.")] +[IsDependentOn(typeof(CkanTask))] +public sealed class RpmRepoTask() : MakeTask("rpm", "repo"); + +[TaskName("rpm-test")] +[TaskDescription("Test the rpm packaging.")] +public sealed class RpmTestTask() : MakeTask("rpm", "test"); + +[TaskName("rpm-clean")] +[TaskDescription("Clean the rpm package output directory.")] +public sealed class RpmCleanTask() : MakeTask("rpm", "clean"); + +public abstract class MakeTask(string location, ProcessArgumentBuilder args = null) : FrostingTask +{ + private string Location { get; } = location; + private ProcessArgumentBuilder Args { get; } = args ?? ""; + + public override void Run(BuildContext context) + { + var exitCode = context.StartProcess("make", new ProcessSettings() { + WorkingDirectory = Location, + Arguments = Args, + EnvironmentVariables = new Dictionary { { "CONFIGURATION", context.BuildConfiguration } } + }); + if (exitCode != 0) + { + throw new Exception("Make failed with exit code: " + exitCode); + } + } +} \ No newline at end of file diff --git a/build/Program.cs b/build/Program.cs new file mode 100644 index 0000000000..9b4fc40637 --- /dev/null +++ b/build/Program.cs @@ -0,0 +1,372 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Cake.Common; +using Cake.Common.Diagnostics; +using Cake.Common.IO; +using Cake.Common.Solution.Project.Properties; +using Cake.Common.Tools.DotNet; +using Cake.Common.Tools.DotNet.Build; +using Cake.Common.Tools.DotNet.Restore; +using Cake.Common.Tools.DotNet.Test; +using Cake.Common.Tools.ILMerge; +using Cake.Common.Tools.ILRepack; +using Cake.Common.Tools.MSBuild; +using Cake.Common.Tools.NUnit; +using Cake.Frosting; + +namespace Build; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .InstallTool(new Uri("nuget:?package=ILRepack&version=2.0.27")) + .InstallTool(new Uri("nuget:?package=NUnit.ConsoleRunner&version=3.16.3")) + .UseContext() + .UseLifetime() + .Run(args); + } +} + +[TaskName("Default")] +[TaskDescription("Build ckan.exe and netkan.exe")] +[IsDependentOn(typeof(CkanTask))] +[IsDependentOn(typeof(NetkanTask))] +public sealed class DefaultTask : FrostingTask; + +[TaskName("Debug")] +[TaskDescription("Build ckan.exe and netkan.exe in Debug configuration")] +[IsDependentOn(typeof(DefaultTask))] +public sealed class DebugTask : FrostingTask; + +[TaskName("Release")] +[TaskDescription("Build ckan.exe and netkan.exe in Release configuration")] +[IsDependentOn(typeof(DefaultTask))] +public sealed class ReleaseTask : FrostingTask; + +[TaskName("Netkan")] +[TaskDescription("Build only netkan.exe")] +[IsDependentOn(typeof(RepackNetkanTask))] +public sealed class NetkanTask : FrostingTask; + +[TaskName("Ckan")] +[TaskDescription("Build only ckan.exe")] +[IsDependentOn(typeof(RepackCkanTask))] +public sealed class CkanTask : FrostingTask; + +[TaskName("Restore")] +[TaskDescription("Intermediate - Download dependencies")] +public sealed class RestoreTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.DotNetRestore(new DotNetRestoreSettings + { + PackagesDirectory = context.Paths.NugetDirectory, + EnvironmentVariables = new Dictionary { { "Configuration", context.BuildConfiguration } } + }); + } +} + +[TaskName("Generate-GlobalAssemblyVersionInfo")] +[TaskDescription("Intermediate - Calculate the version strings for the assembly.")] +public sealed class GenerateGlobalAssemblyVersionInfoTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var metaDirectory = context.Paths.BuildDirectory.Combine("meta"); + context.CreateDirectory(metaDirectory); + + var version = context.GetVersion(); + + context.CreateAssemblyInfo( + metaDirectory.CombineWithFilePath("GlobalAssemblyVersionInfo.cs"), new AssemblyInfoSettings + { + Version = $"{version.Major}.{version.Minor}", + FileVersion = version.HasMeta + ? $"{version.Major}.{version.Minor}.{version.Patch}.{version.Meta}" + : $"{version.Major}.{version.Minor}.{version.Patch}", + InformationalVersion = version.ToString() + }); + } +} + +[TaskName("Build")] +[TaskDescription("Intermediate - Build everything")] +[IsDependentOn(typeof(RestoreTask))] +[IsDependentOn(typeof(GenerateGlobalAssemblyVersionInfoTask))] +public sealed class BuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // dotnet build won't let us compile WinForms on non-Windows, + // fall back to mono + if (context.IsRunningOnWindows()) + { + context.DotNetBuild(context.Solution, new DotNetBuildSettings + { + Configuration = context.BuildConfiguration, + NoRestore = true, + }); + } + else + { + // Use dotnet to build the Core DLL to get the nupkg + // (only created if all TargetFrameworks are built together) + context.DotNetBuild(context.Paths.CoreProject.FullPath, new DotNetBuildSettings + { + Configuration = context.BuildConfiguration, + }); + + // Use Mono to build for net48 since dotnet can't use WinForms on Linux + context.MSBuild(context.Solution, + settings => settings + .SetConfiguration(context.BuildConfiguration) + .SetMaxCpuCount(0) + .WithProperty("TargetFramework", context.BuildNetFramework)); + // Use dotnet to build the stuff Mono can't build + context.DotNetBuild(context.Solution, new DotNetBuildSettings + { + Configuration = "NoGUI", + Framework = "net8.0", + }); + } + } +} + +[TaskName("Repack-Ckan")] +[TaskDescription("Intermediate - Merge all the separate DLLs and EXEs to a single executable.")] +[IsDependentOn(typeof(BuildTask))] +public sealed class RepackCkanTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.CreateDirectory(context.Paths.RepackDirectory.Combine(context.BuildConfiguration)); + + var cmdLineBinDirectory = context.Paths.OutDirectory.Combine("CKAN-CmdLine") + .Combine(context.BuildConfiguration) + .Combine("bin") + .Combine(context.BuildNetFramework); + var assemblyPaths = context.GetFiles($"{cmdLineBinDirectory}/*.dll"); + assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-GUI.exe")); + assemblyPaths.Add(cmdLineBinDirectory.CombineWithFilePath("CKAN-ConsoleUI.exe")); + var cmdlinePath = context.Paths.OutDirectory.Combine("CKAN-CmdLine") + .Combine(context.BuildConfiguration) + .Combine("bin") + .Combine(context.BuildNetFramework); + assemblyPaths.Add(context.GetFiles($"{cmdlinePath}/*/*.resources.dll")); + // Need facade to instantiate types from netstandard2.0 DLLs on Mono + assemblyPaths.Add(context.FacadesDirectory().CombineWithFilePath("netstandard.dll")); + var ckanLogFile = context.Paths.RepackDirectory.Combine(context.BuildConfiguration) + .CombineWithFilePath("ckan.log"); + context.ReportRepacking(context.Paths.CkanFile, ckanLogFile); + context.ILRepack( + context.Paths.CkanFile, + cmdLineBinDirectory.CombineWithFilePath("CKAN-CmdLine.exe"), + assemblyPaths, + new ILRepackSettings + { + Libs = [cmdLineBinDirectory], + TargetPlatform = TargetPlatformVersion.v4, + Parallel = true, + Verbose = false, + SetupProcessSettings = BuildContext.RepackSilently, + Log = ckanLogFile.FullPath, + }); + + var autoupdateBinDirectory = context.Paths.OutDirectory.Combine("CKAN-AutoUpdateHelper") + .Combine(context.BuildConfiguration) + .Combine("bin") + .Combine(context.BuildNetFramework); + var updaterLogFile = context.Paths.RepackDirectory.Combine(context.BuildConfiguration) + .CombineWithFilePath("AutoUpdater.log"); + context.ReportRepacking(context.Paths.UpdaterFile, updaterLogFile); + context.ILRepack( + context.Paths.UpdaterFile, + autoupdateBinDirectory.CombineWithFilePath("CKAN-AutoUpdateHelper.exe"), + context.GetFiles($"{autoupdateBinDirectory}/*/*.resources.dll"), + new ILRepackSettings + { + Libs = [autoupdateBinDirectory], + TargetPlatform = TargetPlatformVersion.v4, + Parallel = true, + Verbose = false, + SetupProcessSettings = BuildContext.RepackSilently, + Log = updaterLogFile.FullPath, + }); + + context.CopyFile(context.Paths.CkanFile, + context.Paths.BuildDirectory.CombineWithFilePath(context.Paths.CkanFile.GetFilename())); + } +} + +[TaskName("Repack-Netkan")] +[TaskDescription("Intermediate - Merge all the separate DLLs and EXEs to a single executable.")] +[IsDependentOn(typeof(BuildTask))] +public sealed class RepackNetkanTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.CreateDirectory(context.Paths.RepackDirectory.Combine(context.BuildConfiguration)); + var netkanBinDirectory = context.Paths.OutDirectory.Combine("CKAN-NetKAN") + .Combine(context.BuildConfiguration) + .Combine("bin") + .Combine(context.BuildNetFramework); + var netkanLogFile = context.Paths.RepackDirectory.Combine(context.BuildConfiguration) + .CombineWithFilePath("netkan.log"); + var assemblyPaths = context.GetFiles($"{netkanBinDirectory}/*.dll"); + // Need facade to instantiate types from netstandard2.0 DLLs on Mono + assemblyPaths.Add(context.FacadesDirectory().CombineWithFilePath("netstandard.dll")); + context.ReportRepacking(context.Paths.NetkanFile, netkanLogFile); + context.ILRepack( + context.Paths.NetkanFile, + netkanBinDirectory.CombineWithFilePath("CKAN-NetKAN.exe"), + assemblyPaths, + new ILRepackSettings + { + Libs = [netkanBinDirectory], + TargetPlatform = TargetPlatformVersion.v4, + Parallel = true, + Verbose = false, + SetupProcessSettings = BuildContext.RepackSilently, + Log = netkanLogFile.FullPath, + } + ); + + context.CopyFile(context.Paths.NetkanFile, + context.Paths.BuildDirectory.CombineWithFilePath(context.Paths.NetkanFile.GetFilename())); + } +} + +[TaskName("Prepare-SignPath")] +[TaskDescription("Create a folder with all artifacts to be signed")] +[IsDependentOn(typeof(RepackCkanTask))] +public sealed class PrepareSignPathTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var targetDir = context.Paths.BuildDirectory.Combine("signpath") + .Combine(context.BuildConfiguration); + context.CreateDirectory(targetDir); + context.CopyFile(context.Paths.CkanFile, targetDir.CombineWithFilePath(context.Paths.CkanFile.GetFilename())); + context.CopyFile(context.Paths.UpdaterFile, targetDir.CombineWithFilePath(context.Paths.UpdaterFile.GetFilename())); + context.CopyFile(context.Paths.NupkgFile, targetDir.CombineWithFilePath(context.Paths.NupkgFile.GetFilename())); + } +} + +[TaskName("Test")] +[TaskDescription("Run tests after compilation.")] +[IsDependentOn(typeof(DefaultTask))] +[IsDependentOn(typeof(TestOnlyTask))] +public sealed class TestTask : FrostingTask; + +[TaskName("Test+Only")] +[TaskDescription("Run tests without compiling.")] +[IsDependentOn(typeof(TestExecutablesOnlyTask))] +[IsDependentOn(typeof(TestUnitTestsOnlyTask))] +public sealed class TestOnlyTask : FrostingTask; + +[TaskName("Test-Executables+Only")] +[TaskDescription("Intermediate - Test executables without compiling.")] +[IsDependentOn(typeof(TestCkanExecutableOnlyTask))] +[IsDependentOn(typeof(TestNetkanExecutableOnlyTask))] +public sealed class TestExecutablesOnlyTask : FrostingTask; + +[TaskName("Test-CkanExecutable+Only")] +[TaskDescription("Intermediate - Test ckan.exe without compiling.")] +public sealed class TestCkanExecutableOnlyTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var output = context.RunExecutable(context.Paths.CkanFile, "version").FirstOrDefault(); + if (output != $"v{context.GetVersion()}") + { + throw new Exception($"ckan.exe smoke test failed: {output}"); + } + } +} + +[TaskName("Test-NetkanExecutable+Only")] +[TaskDescription("Intermediate - Test netkan.exe without compiling.")] +public sealed class TestNetkanExecutableOnlyTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var output = context.RunExecutable(context.Paths.NetkanFile, "--version").FirstOrDefault(); + if (output != $"v{context.GetVersion()}") + { + throw new Exception($"netkan.exe smoke test failed: {output}"); + } + } +} + +[TaskName("Test-UnitTests+Only")] +[TaskDescription("Intermediate - Run unit tests without compiling.")] +public sealed class TestUnitTestsOnlyTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var where = context.Argument("where", null); + var nunitOutputDirectory = context.Paths.BuildDirectory.Combine("test") + .Combine("nunit"); + context.CreateDirectory(nunitOutputDirectory); + // Only Mono's msbuild can handle WinForms on Linux, + // but dotnet build can handle multi-targeting on Windows + if (context.IsRunningOnWindows()) + { + context.DotNetTest(context.Solution, new DotNetTestSettings + { + Configuration = context.BuildConfiguration, + NoRestore = true, + NoBuild = true, + NoLogo = true, + Filter = where, + ResultsDirectory = nunitOutputDirectory, + Verbosity = DotNetVerbosity.Minimal, + }); + } + else + { + context.DotNetTest(context.Solution, new DotNetTestSettings + { + Configuration = "NoGUI", + Framework = "net8.0", + NoRestore = true, + NoBuild = true, + NoLogo = true, + Filter = where, + ResultsDirectory = nunitOutputDirectory, + Verbosity = DotNetVerbosity.Minimal, + }); + var testFile = context.Paths.OutDirectory.Combine("CKAN.Tests") + .Combine(context.BuildConfiguration) + .Combine("bin") + .Combine(context.BuildNetFramework) + .CombineWithFilePath("CKAN.Tests.dll"); + context.NUnit3(testFile.FullPath, new NUnit3Settings + { + Configuration = context.BuildConfiguration, + Where = where, + Work = nunitOutputDirectory, + NoHeader = true, + // Work around System.Runtime.Remoting.RemotingException : Tcp transport error. + Process = NUnit3ProcessOption.InProcess, + }); + } + } +} + +[TaskName("Version")] +[TaskDescription("Print the current CKAN version.")] +public sealed class VersionTask : FrostingTask +{ + public override void Run(BuildContext context) + { + using (context.NormalVerbosity()) + { + context.Information(context.GetVersion().ToString()); + } + } +} \ No newline at end of file diff --git a/cake.config b/cake.config deleted file mode 100644 index abdda5f965..0000000000 --- a/cake.config +++ /dev/null @@ -1,4 +0,0 @@ -[Paths] -Tools=./_build/tools -Addins=./_build/cake/addins -Modules=./_build/cake/modules From 7681a39c9c0ef87d4ea8434c4497ad41f9601b48 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 22:51:44 -0800 Subject: [PATCH 02/16] fix: downgrade build script to net8.0 like rest of project --- build/Build.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Build.csproj b/build/Build.csproj index c2e3091dfb..da444eebd9 100644 --- a/build/Build.csproj +++ b/build/Build.csproj @@ -1,7 +1,7 @@ Exe - net9.0 + net8.0 $(MSBuildProjectDirectory) From 0a283696c2e84a3cd84a2b4a26767bdbf2136e8a Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:08:40 -0800 Subject: [PATCH 03/16] fix: update ci --- .github/workflows/build.yml | 8 ++++---- .github/workflows/deploy.yml | 10 +++++----- .github/workflows/release.yml | 10 +++++----- .github/workflows/test.yml | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd77c7ce40..f364b42ef1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,12 +22,12 @@ jobs: uses: actions/cache@v4 with: path: _build/tools - key: build-tools-${{ hashFiles('build', 'build.ps1', 'build.cake') }} + key: build-tools-${{ hashFiles('build.sh', 'build.ps1', 'build/*') }} - name: Restore cache for _build/cake uses: actions/cache@v4 with: path: _build/cake - key: build-cake-${{ hashFiles('build.cake') }} + key: build-cake-${{ hashFiles('build/*') }} - name: Restore cache for _build/lib/nuget uses: actions/cache@v4 with: @@ -36,7 +36,7 @@ jobs: ~/.nuget/packages key: nuget-oldref-modules-${{ hashFiles('**/packages.config') }}-${{ hashFiles('**/*.csproj') }} - name: Build ckan.exe and netkan.exe - run: ./build --configuration=${{ inputs.configuration }} + run: ./build.sh --configuration=${{ inputs.configuration }} - name: Upload repack artifact id: upload-repack-artifact uses: actions/upload-artifact@v4 @@ -52,7 +52,7 @@ jobs: retention-days: 1 - name: Bundle assets for signing if: inputs.configuration == 'Release' - run: ./build Prepare-SignPath --configuration=${{ inputs.configuration }} --exclusive + run: ./build.sh -t Prepare-SignPath --configuration=${{ inputs.configuration }} --exclusive - name: Upload unsigned artifact id: upload-unsigned-artifact if: inputs.configuration == 'Release' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1e21510dae..8c94126dd6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -104,7 +104,7 @@ jobs: name: Release-repack-unsigned path: _build/repack/ - name: Build dmg - run: ./build osx --configuration=Release --exclusive + run: ./build.sh -t osx --configuration=Release --exclusive - name: Push dmg to S3 run: aws s3 cp _build/osx/CKAN.dmg s3://${AWS_S3_BUCKET} --follow-symlinks @@ -137,7 +137,7 @@ jobs: - name: Build deb env: CODENAME: nightly - run: ./build deb --configuration=Release --exclusive + run: ./build.sh -t deb --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -149,7 +149,7 @@ jobs: env: CODENAME: nightly DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build deb-sign --configuration=Release --exclusive + run: ./build.sh -t deb-sign --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push deb to S3 run: aws s3 sync _build/deb/apt-repo-root s3://${AWS_S3_BUCKET}/deb --follow-symlinks @@ -194,7 +194,7 @@ jobs: mkdir -p _build/repack/Release cp _build/signed/ckan.exe _build/repack/Release - name: Build rpm - run: ./build rpm --configuration=Release --exclusive + run: ./build.sh -t rpm --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -206,7 +206,7 @@ jobs: env: CODENAME: nightly DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build rpm-repo --configuration=Release --exclusive + run: ./build.sh -t rpm-repo --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push nightly PRM repo to S3 run: aws s3 sync _build/rpm/repo s3://${AWS_S3_BUCKET}/rpm/nightly --follow-symlinks diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53a888048c..a8fde395a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: name: Release-repack-unsigned path: _build/repack/ - name: Build dmg - run: ./build osx --configuration=Release --exclusive + run: ./build.sh -t osx --configuration=Release --exclusive - name: Upload OSX release asset run: gh release upload ${{ github.event.release.tag_name }} _build/osx/CKAN.dmg env: @@ -79,7 +79,7 @@ jobs: - name: Build deb env: CODENAME: stable - run: ./build deb --configuration=Release --exclusive + run: ./build.sh -t deb --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -91,7 +91,7 @@ jobs: env: CODENAME: stable DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build deb-sign --configuration=Release --exclusive + run: ./build.sh -t deb-sign --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push deb to S3 run: aws s3 sync _build/deb/apt-repo-root s3://${AWS_S3_BUCKET}/deb --follow-symlinks @@ -130,7 +130,7 @@ jobs: VERSION=$(echo "${{ github.event.release.tag_name }}" | tr -d v) echo "RPM_VERSION=${VERSION}.$(date +'%g%j')" >> $GITHUB_ENV - name: Build rpm - run: ./build rpm --configuration=Release --exclusive + run: ./build.sh -t rpm --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -141,7 +141,7 @@ jobs: env: CODENAME: stable DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build rpm-repo --configuration=Release --exclusive + run: ./build.sh -t rpm-repo --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push stable RPM repo to S3 run: aws s3 sync _build/rpm/repo s3://${AWS_S3_BUCKET}/rpm/stable --follow-symlinks diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c08d738a5..7761a05f7a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: name: Debug-repack-unsigned path: _build/repack/ - name: Run tests - run: xvfb-run ./build test+only --configuration=Debug --where="Category!=FlakyNetwork" + run: xvfb-run ./build.sh -t test+only --configuration=Debug --where="Category!=FlakyNetwork" # notify: # needs: From 3c1c6528d1a41af14f22ebdaf0da59efbf89a085 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:17:13 -0800 Subject: [PATCH 04/16] fix: restore in correct directory --- build/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Program.cs b/build/Program.cs index 9b4fc40637..f814996ccf 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -64,6 +64,7 @@ public override void Run(BuildContext context) { context.DotNetRestore(new DotNetRestoreSettings { + WorkingDirectory = context.Paths.RootDirectory, PackagesDirectory = context.Paths.NugetDirectory, EnvironmentVariables = new Dictionary { { "Configuration", context.BuildConfiguration } } }); From 6165ef6e73c9e0587cec4e0a2f8de226ad241439 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:44:17 -0800 Subject: [PATCH 05/16] docs: update build instructions --- doc/building.md | 56 ++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/doc/building.md b/doc/building.md index f2c76a7044..38741c2c4c 100644 --- a/doc/building.md +++ b/doc/building.md @@ -9,7 +9,7 @@ how the CKAN build system currently works and how changes to it should be made. ```plain > git clone https://github.com/KSP-CKAN/CKAN > cd CKAN -> ./build +> ./build.sh > _build/ckan.exe version v1.23.45-dev+abcdef > _build/netkan.exe --version @@ -24,7 +24,7 @@ The following are the minimum requirements to build CKAN and NetKAN from source: - A POSIX compliant shell interpreter at `/bin/sh` - [Mono](http://www.mono-project.com/) >= 4.0.0 -- [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) +- [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) SDK - [cURL](https://curl.haxx.se/) accessible in PATH ### Windows systems @@ -32,7 +32,7 @@ The following are the minimum requirements to build CKAN and NetKAN from source: - PowerShell - MSBuild (usually by installing [Visual Studio](https://www.visualstudio.com/vs/community/)) - [.NET Framework >= 4.8.0](https://dotnet.microsoft.com/en-us/download/dotnet-framework) -- [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) +- [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) ## Build directory @@ -47,10 +47,6 @@ that are output outside this directory should be considered a bug. The `_build` directory contains several subdirectories for different purposes. Nothing should be output to the `_build` directory itself, instead use one of the existing or create a new subdirectory. -- `_build/cake`: Used to store packages used by Cake. Use of this directory by Cake is controlled by the `cake.config` - file in the top-level directory. - - `_build/cake/addins`: Used to store [Cake addins](http://cakebuild.net/addins). - - `_build/cake/modules`: Used to store Cake modules. - `_build/lib`: Used to store packages that are *automatically* resolved by a package manager. - `_build/lib/nuget`: Used to store packages that are automatically resolved by NuGet. - `_build/meta`: Used to store source code that is dynamically generated by the build system. This is primarily used to @@ -67,32 +63,7 @@ directory itself, instead use one of the existing or create a new subdirectory. - `_build/repack`: Used to store the output of [ILRepack](https://github.com/gluck/il-repack). - `_build/test`: Used to store the output of test runners. - `_build/test/nunit`: Used to store the output of the NUnit test runner. -- `_build/tools`: Used to store tools downloaded by the bootstraper or Cake. - -## Bootstrapper Scripts - -The primary entry point to the CKAN build system are the two bootstraper scripts in the top level directory `build` and -`build.ps1`. Developers use these scripts for all build related operations. - -`build` is POSIX shell script for use on Unix-like systems (BSD, Linux, macOS, etc.). `build` is executed by `/bin/sh` -which is expected to be a POSIX compatible shell. It is quite intentionally **not** a bash shell script and as such -contains no [bashisms](https://en.wiktionary.org/wiki/bashism). It can therefore be used on systems where `/bin/sh` is -not bash but instead an alternative shell interpreter like dash. Any changes to this script should be careful not to -introduce such bashisms that would break this compatibility. - -`build.ps1` is a PowerShell script for use on Windows systems. Users on Windows *must* use PowerShell 3.0 or later; - the old `cmd.exe` interpreter is not supported. - -Both of these scripts are used to "bootstrap" the build environment. They function identically and as such we will -treat them as a singular script (`./build`) for all platforms. The basic operation of the bootstrap scripts are to: - -1. Download [NuGet](https://www.nuget.org/) -2. Install `Cake.Tool` for `dotnet` -3. Parse the command line arguments -4. Execute `dotnet cake` with the appropriate command line arguments -5. Quit with the exit code generated by Cake - -The specifics of each operations are given in the following sections. +- `build/tools`: Used to store tools downloaded by Cake. ## NuGet @@ -100,7 +71,7 @@ NuGet is the standard .NET package manager. Since nearly every useful .NET tool it can be used to bootstrap the rest of the build system. Specifically the bootstrapper scripts are used to download NuGet, which Cake then uses to install `Cake.SemVer`, `semver`, `Cake.Docker`, `ILRepack`, and `NUnit.ConsoleRunner`. -Installation of project dependencies is handled by `dotnet restore`. +Installation of project dependencies is handled by `dotnet restore`, which is automatically invoked by the build script. ## Cake @@ -109,25 +80,24 @@ After Cake is installed by NuGet, the bootstrapper scripts then parse the comman The bootstrapper scripts use the following command line: ```plain -./build [] [...] +./build.sh [-t ] [...] ``` Which is then used to execute Cake with the following command line: ```plain -cake.exe build.cake [--target=] [...] +dotnet run --project ./build/Build.csproj -- [-t ] [...] ``` -The first argument is the Cake script to execute. This is always `build.cake` and does not ever need to be changed. The +The first argument is the Cake project to execute. This is always `build` and does not ever need to be changed. The second argument is the `--target` argument which tells Cake which task in `build.cake` to execute. If not provided it defaults to the (appropriately named) `Default` task. Finally, the bootstrapper scripts will pass any additional arguments to Cake as is. ### `build.cake` -`build.cake` is the Cake script which does most of the heavy lifting for the build. Cake scripts are C#-like files -which are dynamically compiled by the Roslyn compiler. Pretty much all the same rules as standard C# apply with the -exception of Cake-specific [preprocessor directives](http://cakebuild.net/docs/fundamentals/preprocessor-directives). +The `build` folder is the Cake Frosting project which does most of the heavy lifting for the build. Cake Frosting +projects are normal command-line C# projects, so all the same rules as standard C# apply. The CKAN build script is fairly simply and self-explanatory. Some conventions of note: @@ -161,7 +131,7 @@ The purpose of each defined task is as follows: #### Helper Methods -`build.cake` also contains a couple of helper methods. +The build script also contains a couple of helper methods. - `GetVersion()`: Read the most recent version number from `CHANGELOG.md` and and attach a short version (12-character) of the current git commit hash if available. The output is a @@ -213,8 +183,8 @@ push. GitHub's operation is pretty simple: - A bunch of packages are installed to make the build work. - Some commands are executed to simulate a graphical environment for testing. -- The solution is built using `./build --configuration=$BUILD_CONFIGURATION` -- The solution is tested using `./build test+only --configuration=$BUILD_CONFIGURATION --where="Category!=FlakyNetwork"` +- The solution is built using `./build.sh --configuration=$BUILD_CONFIGURATION` +- The solution is tested using `./build.sh -t test+only --configuration=$BUILD_CONFIGURATION --where="Category!=FlakyNetwork"` - The `--where="Category!=FlakyNetwork"` argument is used to not execute tests that rely upon a network connection and could thus behave nondeterministically (frustrating behavior for a CI system). - The built executables (`ckan.exe` and `netkan.exe`) are uploaded to Amazon S3 if the following conditions are met: From 7ede52248268286dbe2f40343b0787117a13e0d6 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:44:39 -0800 Subject: [PATCH 06/16] fix: add build csproj to sln to get better intellisense --- CKAN.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CKAN.sln b/CKAN.sln index 864ee92723..abe66284d4 100644 --- a/CKAN.sln +++ b/CKAN.sln @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CKAN-netkan", "Netkan\CKAN- EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4F41255E-8BC1-465B-82D5-1C5665BC099A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build", "build\Build.csproj", "{B3D22D01-132E-4D78-BA9E-FAC2AD02F2E1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 2cb80cd0e5cf67f0549cd31deb84b0f859ef92da Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:52:35 -0800 Subject: [PATCH 07/16] fix: use build.sh instead of build everywhere --- Dockerfile | 2 +- debian/Makefile | 2 +- macosx/Makefile | 2 +- rpm/Makefile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index bf97b87a91..20ad1dd2eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,7 +56,7 @@ COPY . /source WORKDIR /source ARG config ENV config ${config:-Release} -RUN ./build --configuration=${config} +RUN ./build.sh --configuration=${config} RUN mkdir /build RUN cp _build/repack/${config}/ckan.exe /build/ckan.exe ENTRYPOINT ["/root/entrypoint.sh"] diff --git a/debian/Makefile b/debian/Makefile index 53fd225937..f32285bf99 100644 --- a/debian/Makefile +++ b/debian/Makefile @@ -95,7 +95,7 @@ $(EXEDEST): $(EXESRC) umask 0022 && cp $< $@ $(EXESRC): - cd .. && ./build --configuration=$(CONFIGURATION) + cd .. && ./build.sh --configuration=$(CONFIGURATION) $(CONTROLDEST): $(CONTROLSRC) $(CHANGELOGSRC) umask 0022 && mkdir -p $(shell dirname $@) diff --git a/macosx/Makefile b/macosx/Makefile index cc85209021..ad975f59a5 100644 --- a/macosx/Makefile +++ b/macosx/Makefile @@ -27,7 +27,7 @@ $(EXEDEST): $(EXESRC) ln $< $@ $(EXESRC): - cd .. && ./build --configuration=Release + cd .. && ./build.sh --configuration=Release $(SCRIPTDEST): $(SCRIPTSRC) mkdir -p $(shell dirname $@) diff --git a/rpm/Makefile b/rpm/Makefile index ab6022145a..c6f8e46fc9 100644 --- a/rpm/Makefile +++ b/rpm/Makefile @@ -51,7 +51,7 @@ $(REPORPM): $(RPM) $(REPOFILE) --addsign $@ $(EXESRC): - cd .. && ./build --configuration=$(CONFIGURATION) + cd .. && ./build.sh --configuration=$(CONFIGURATION) clean: rm -r "$(TOPDIR)" From e5d02e100bc344732b8519cb869189cc591c9413 Mon Sep 17 00:00:00 2001 From: Lewis Date: Fri, 21 Feb 2025 23:57:45 -0800 Subject: [PATCH 08/16] chore: add rider config for multi-platform development --- .gitignore | 79 +++++++++++++++++++ .idea/.idea.CKAN/.idea/indexLayout.xml | 8 ++ .../.idea/projectSettingsUpdater.xml | 7 ++ .../runConfigurations/Run_Build__Debug_.xml | 17 ++++ .idea/.idea.CKAN/.idea/vcs.xml | 6 ++ 5 files changed, 117 insertions(+) create mode 100644 .idea/.idea.CKAN/.idea/indexLayout.xml create mode 100644 .idea/.idea.CKAN/.idea/projectSettingsUpdater.xml create mode 100644 .idea/.idea.CKAN/.idea/runConfigurations/Run_Build__Debug_.xml create mode 100644 .idea/.idea.CKAN/.idea/vcs.xml diff --git a/.gitignore b/.gitignore index 54f7fcdccb..50f02605c9 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,82 @@ Network Trash Folder Temporary Items .apdisk +### Rider template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + diff --git a/.idea/.idea.CKAN/.idea/indexLayout.xml b/.idea/.idea.CKAN/.idea/indexLayout.xml new file mode 100644 index 0000000000..7b08163ceb --- /dev/null +++ b/.idea/.idea.CKAN/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.CKAN/.idea/projectSettingsUpdater.xml b/.idea/.idea.CKAN/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000000..64af657f5c --- /dev/null +++ b/.idea/.idea.CKAN/.idea/projectSettingsUpdater.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.CKAN/.idea/runConfigurations/Run_Build__Debug_.xml b/.idea/.idea.CKAN/.idea/runConfigurations/Run_Build__Debug_.xml new file mode 100644 index 0000000000..97e0d127f2 --- /dev/null +++ b/.idea/.idea.CKAN/.idea/runConfigurations/Run_Build__Debug_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.CKAN/.idea/vcs.xml b/.idea/.idea.CKAN/.idea/vcs.xml new file mode 100644 index 0000000000..94a25f7f4c --- /dev/null +++ b/.idea/.idea.CKAN/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 393e87a75cd5e8cbe89107385b2d14af451dd79a Mon Sep 17 00:00:00 2001 From: Lewis Date: Sat, 22 Feb 2025 00:26:08 -0800 Subject: [PATCH 09/16] fix: move build script artifacts into `_build` --- build/Build.csproj | 9 ++++++++- build/Program.cs | 8 ++++++++ doc/building.md | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/build/Build.csproj b/build/Build.csproj index da444eebd9..b8d5946802 100644 --- a/build/Build.csproj +++ b/build/Build.csproj @@ -1,9 +1,15 @@ - + Exe net8.0 $(MSBuildProjectDirectory) + Build + ..\_build\out\$(AssemblyName)\$(Configuration)\bin\ + ..\_build\out\$(AssemblyName)\VSCodeIDE\bin\ + ..\_build\out\$(AssemblyName)\$(Configuration)\obj\ + ..\_build\out\$(AssemblyName)\VSCodeIDE\obj\ + @@ -12,4 +18,5 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/build/Program.cs b/build/Program.cs index f814996ccf..3c575c649d 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -13,6 +13,7 @@ using Cake.Common.Tools.ILRepack; using Cake.Common.Tools.MSBuild; using Cake.Common.Tools.NUnit; +using Cake.Core.IO; using Cake.Frosting; namespace Build; @@ -22,6 +23,13 @@ public static class Program public static int Main(string[] args) { return new CakeHost() + .ConfigureServices(services => + { + services.UseToolPath(new DirectoryPath(Environment.CurrentDirectory) + .GetParent() + .Combine("_build") + .Combine("tools")); + }) .InstallTool(new Uri("nuget:?package=ILRepack&version=2.0.27")) .InstallTool(new Uri("nuget:?package=NUnit.ConsoleRunner&version=3.16.3")) .UseContext() diff --git a/doc/building.md b/doc/building.md index 38741c2c4c..55d25289fd 100644 --- a/doc/building.md +++ b/doc/building.md @@ -63,7 +63,7 @@ directory itself, instead use one of the existing or create a new subdirectory. - `_build/repack`: Used to store the output of [ILRepack](https://github.com/gluck/il-repack). - `_build/test`: Used to store the output of test runners. - `_build/test/nunit`: Used to store the output of the NUnit test runner. -- `build/tools`: Used to store tools downloaded by Cake. +- `_build/tools`: Used to store tools downloaded by Cake. ## NuGet From 9cf263343de9ef72cb17839feff0c54cf8d72326 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sat, 22 Feb 2025 11:45:04 -0800 Subject: [PATCH 10/16] fix(build): add support for older dotnet 8.0.113 --- build/BuildContext.cs | 2 +- build/Program.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build/BuildContext.cs b/build/BuildContext.cs index 962f1d63ec..1819d2f412 100644 --- a/build/BuildContext.cs +++ b/build/BuildContext.cs @@ -125,7 +125,7 @@ public string GetQuote(FilePath file) var quotes = System.IO.File .ReadAllText(file.FullPath) - .Split(['%'], StringSplitOptions.RemoveEmptyEntries); + .Split("%", StringSplitOptions.RemoveEmptyEntries); return quotes.Length > 0 ? quotes[new Random().Next(quotes.Length)] : null; } diff --git a/build/Program.cs b/build/Program.cs index 3c575c649d..315ed50d14 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -130,11 +130,12 @@ public override void Run(BuildContext context) }); // Use Mono to build for net48 since dotnet can't use WinForms on Linux - context.MSBuild(context.Solution, - settings => settings - .SetConfiguration(context.BuildConfiguration) - .SetMaxCpuCount(0) - .WithProperty("TargetFramework", context.BuildNetFramework)); + context.MSBuild(context.Solution, new MSBuildSettings + { + Configuration = context.BuildConfiguration, + MaxCpuCount = 0, + Properties = { { "TargetFramework", [context.BuildNetFramework] } }, + }); // Use dotnet to build the stuff Mono can't build context.DotNetBuild(context.Solution, new DotNetBuildSettings { From 6bc8876afd58a92cd9c083f31b672eb4453fc1a7 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sat, 22 Feb 2025 16:52:07 -0800 Subject: [PATCH 11/16] fix: apply Release/Debug aliases before calculating paths --- build/BuildContext.cs | 18 +++++++++++++++++- build/BuildLifetime.cs | 16 ---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build/BuildContext.cs b/build/BuildContext.cs index 1819d2f412..3dd553f57c 100644 --- a/build/BuildContext.cs +++ b/build/BuildContext.cs @@ -30,9 +30,25 @@ public BuildContext(ICakeContext context) var rootDir = context.Environment.WorkingDirectory.GetParent(); Target = context.Argument("target", "Default"); - BuildConfiguration = context.Argument("configuration", "Debug"); + BuildConfiguration = context.Argument("configuration", null); Solution = context.Argument("solution", rootDir.CombineWithFilePath("CKAN.sln").FullPath); + if (string.Equals(Target, "Release", StringComparison.OrdinalIgnoreCase)) + { + if (BuildConfiguration != null) + context.Warning($"Ignoring configuration argument: '{BuildConfiguration}'"); + + BuildConfiguration = "Release"; + } + else if (string.Equals(Target, "Debug", StringComparison.OrdinalIgnoreCase)) + { + if (BuildConfiguration != null) + context.Warning($"Ignoring configuration argument: '{BuildConfiguration}'"); + + BuildConfiguration = "Debug"; + } + + BuildConfiguration ??= "Debug"; Paths = new BuildPaths(rootDir, BuildConfiguration, GetVersion(false)); } diff --git a/build/BuildLifetime.cs b/build/BuildLifetime.cs index 59362a498b..877b1f5061 100644 --- a/build/BuildLifetime.cs +++ b/build/BuildLifetime.cs @@ -10,22 +10,6 @@ public class BuildLifetime : FrostingLifetime { public override void Setup(BuildContext context, ISetupContext info) { - var argConfiguration = context.Argument("configuration", null); - - if (string.Equals(context.Target, "Release", StringComparison.OrdinalIgnoreCase)) - { - if (argConfiguration != null) - context.Warning($"Ignoring configuration argument: '{argConfiguration}'"); - - context.BuildConfiguration = "Release"; - } - else if (string.Equals(context.Target, "Debug", StringComparison.OrdinalIgnoreCase)) - { - if (argConfiguration != null) - context.Warning($"Ignoring configuration argument: '{argConfiguration}'"); - - context.BuildConfiguration = "Debug"; - } } public override void Teardown(BuildContext context, ITeardownContext info) From 23c9ffec68fa407a6761b5c74fc1b98c5b13acc5 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Sun, 23 Feb 2025 12:42:29 -0600 Subject: [PATCH 12/16] Enable nullable references in build project --- .editorconfig | 3 +++ build/Build.csproj | 6 +++++- build/BuildContext.cs | 35 +++++++++++++++++++++-------------- build/BuildLifetime.cs | 11 ++++++----- build/MakeTasks.cs | 7 ++++--- build/Program.cs | 13 +++++++------ 6 files changed, 46 insertions(+), 29 deletions(-) diff --git a/.editorconfig b/.editorconfig index da701fb2dd..9e981b3144 100644 --- a/.editorconfig +++ b/.editorconfig @@ -57,5 +57,8 @@ dotnet_diagnostic.IDE0090.severity = none # Allow namespaces to be independent of folder names dotnet_diagnostic.IDE0130.severity = none +# Allow file-scoped namespaces +dotnet_diagnostic.IDE0160.severity = none + # Who cares if it's a JSON formatted string, in a test? dotnet_diagnostic.JSON002.severity = none diff --git a/build/Build.csproj b/build/Build.csproj index b8d5946802..da050b5902 100644 --- a/build/Build.csproj +++ b/build/Build.csproj @@ -8,6 +8,10 @@ ..\_build\out\$(AssemblyName)\VSCodeIDE\bin\ ..\_build\out\$(AssemblyName)\$(Configuration)\obj\ ..\_build\out\$(AssemblyName)\VSCodeIDE\obj\ + false + 12 + enable + true @@ -19,4 +23,4 @@ - \ No newline at end of file + diff --git a/build/BuildContext.cs b/build/BuildContext.cs index 3dd553f57c..ad7c8bdfb0 100644 --- a/build/BuildContext.cs +++ b/build/BuildContext.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; + using Cake.Common; using Cake.Common.Diagnostics; using Cake.Common.IO; @@ -19,7 +20,7 @@ public partial class BuildContext : FrostingContext public string Target { get; } // Named to avoid conflict with ICakeContext.Configuration - public string BuildConfiguration { get; set; } + public string? BuildConfiguration { get; set; } public string Solution { get; } public BuildPaths Paths { get; } @@ -28,26 +29,30 @@ public BuildContext(ICakeContext context) : base(context) { var rootDir = context.Environment.WorkingDirectory.GetParent(); - + Target = context.Argument("target", "Default"); - BuildConfiguration = context.Argument("configuration", null); + BuildConfiguration = context.Argument("configuration", null); Solution = context.Argument("solution", rootDir.CombineWithFilePath("CKAN.sln").FullPath); if (string.Equals(Target, "Release", StringComparison.OrdinalIgnoreCase)) { if (BuildConfiguration != null) + { context.Warning($"Ignoring configuration argument: '{BuildConfiguration}'"); + } BuildConfiguration = "Release"; } else if (string.Equals(Target, "Debug", StringComparison.OrdinalIgnoreCase)) { if (BuildConfiguration != null) + { context.Warning($"Ignoring configuration argument: '{BuildConfiguration}'"); + } BuildConfiguration = "Debug"; } - + BuildConfiguration ??= "Debug"; Paths = new BuildPaths(rootDir, BuildConfiguration, GetVersion(false)); } @@ -59,11 +64,11 @@ public SemVersion GetVersion(bool withBuild = true) var versionMatch = System.IO.File .ReadAllLines(rootDirectory.CombineWithFilePath("CHANGELOG.md").FullPath) .Select(i => VersionRegex().Match(i)) - .FirstOrDefault(i => i.Success); + .First(i => i.Success); if (!SemVersion.TryParse(versionMatch.Groups["version"].Value, out var version)) { - throw new System.Exception("Could not parse version from CHANGELOG.md"); + throw new Exception("Could not parse version from CHANGELOG.md"); } if (withBuild && this.DirectoryExists(rootDirectory.Combine(".git"))) @@ -109,7 +114,7 @@ public DirectoryPath FacadesDirectory() { monoLib = new DirectoryPath("/usr").Combine("lib"); } - + return monoLib .Combine("mono") .Combine("4.8-api") @@ -135,9 +140,12 @@ public static void RepackSilently(ProcessSettings settings) .SetRedirectStandardError(true) .SetRedirectedStandardErrorHandler(s => ""); - public string GetQuote(FilePath file) + public string? GetQuote(FilePath file) { - if (!this.FileExists(file)) return null; + if (!this.FileExists(file)) + { + return null; + } var quotes = System.IO.File .ReadAllText(file.FullPath) @@ -145,21 +153,20 @@ public string GetQuote(FilePath file) return quotes.Length > 0 ? quotes[new Random().Next(quotes.Length)] : null; } - + public IEnumerable RunExecutable(FilePath executable, string arguments) { - IEnumerable output; var exitCode = this.StartProcess( executable, new ProcessSettings { Arguments = arguments, RedirectStandardOutput = true }, - out output + out IEnumerable output ); if (exitCode != 0) { throw new Exception("Process failed with exit code: " + exitCode); } - + return output; } -} \ No newline at end of file +} diff --git a/build/BuildLifetime.cs b/build/BuildLifetime.cs index 877b1f5061..f8520c7e09 100644 --- a/build/BuildLifetime.cs +++ b/build/BuildLifetime.cs @@ -1,5 +1,3 @@ -using System; -using Cake.Common; using Cake.Common.Diagnostics; using Cake.Core; using Cake.Frosting; @@ -15,11 +13,14 @@ public override void Setup(BuildContext context, ISetupContext info) public override void Teardown(BuildContext context, ITeardownContext info) { var quote = context.GetQuote(context.Paths.RootDirectory.CombineWithFilePath("quotes.txt")); - if (quote == null) return; - + if (quote == null) + { + return; + } + using (context.NormalVerbosity()) { context.Information(quote); } } -} \ No newline at end of file +} diff --git a/build/MakeTasks.cs b/build/MakeTasks.cs index f5a0549132..b9237134fe 100644 --- a/build/MakeTasks.cs +++ b/build/MakeTasks.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + using Cake.Common; using Cake.Core.IO; using Cake.Frosting; @@ -52,7 +53,7 @@ public sealed class RpmTestTask() : MakeTask("rpm", "test"); [TaskDescription("Clean the rpm package output directory.")] public sealed class RpmCleanTask() : MakeTask("rpm", "clean"); -public abstract class MakeTask(string location, ProcessArgumentBuilder args = null) : FrostingTask +public abstract class MakeTask(string location, ProcessArgumentBuilder? args = null) : FrostingTask { private string Location { get; } = location; private ProcessArgumentBuilder Args { get; } = args ?? ""; @@ -62,11 +63,11 @@ public override void Run(BuildContext context) var exitCode = context.StartProcess("make", new ProcessSettings() { WorkingDirectory = Location, Arguments = Args, - EnvironmentVariables = new Dictionary { { "CONFIGURATION", context.BuildConfiguration } } + EnvironmentVariables = new Dictionary { { "CONFIGURATION", context.BuildConfiguration } } }); if (exitCode != 0) { throw new Exception("Make failed with exit code: " + exitCode); } } -} \ No newline at end of file +} diff --git a/build/Program.cs b/build/Program.cs index 315ed50d14..2737aaf1db 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; + using Cake.Common; using Cake.Common.Diagnostics; using Cake.Common.IO; @@ -74,7 +75,7 @@ public override void Run(BuildContext context) { WorkingDirectory = context.Paths.RootDirectory, PackagesDirectory = context.Paths.NugetDirectory, - EnvironmentVariables = new Dictionary { { "Configuration", context.BuildConfiguration } } + EnvironmentVariables = new Dictionary { { "Configuration", context.BuildConfiguration } } }); } } @@ -87,7 +88,7 @@ public override void Run(BuildContext context) { var metaDirectory = context.Paths.BuildDirectory.Combine("meta"); context.CreateDirectory(metaDirectory); - + var version = context.GetVersion(); context.CreateAssemblyInfo( @@ -128,7 +129,7 @@ public override void Run(BuildContext context) { Configuration = context.BuildConfiguration, }); - + // Use Mono to build for net48 since dotnet can't use WinForms on Linux context.MSBuild(context.Solution, new MSBuildSettings { @@ -154,7 +155,7 @@ public sealed class RepackCkanTask : FrostingTask public override void Run(BuildContext context) { context.CreateDirectory(context.Paths.RepackDirectory.Combine(context.BuildConfiguration)); - + var cmdLineBinDirectory = context.Paths.OutDirectory.Combine("CKAN-CmdLine") .Combine(context.BuildConfiguration) .Combine("bin") @@ -318,7 +319,7 @@ public sealed class TestUnitTestsOnlyTask : FrostingTask { public override void Run(BuildContext context) { - var where = context.Argument("where", null); + var where = context.Argument("where", null); var nunitOutputDirectory = context.Paths.BuildDirectory.Combine("test") .Combine("nunit"); context.CreateDirectory(nunitOutputDirectory); @@ -379,4 +380,4 @@ public override void Run(BuildContext context) context.Information(context.GetVersion().ToString()); } } -} \ No newline at end of file +} From ca7ca2d267505983dcbb6d290e59e3878dc88a45 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sun, 23 Feb 2025 13:34:43 -0800 Subject: [PATCH 13/16] fix: add back `-t` auto-insertion --- .github/workflows/build.yml | 2 +- .github/workflows/deploy.yml | 10 ++++----- .github/workflows/release.yml | 10 ++++----- .github/workflows/test.yml | 2 +- build.ps1 | 40 +++++++++++++++++++++++++++++++++-- build.sh | 40 ++++++++++++++++++++++++++++++++++- doc/building.md | 2 +- 7 files changed, 90 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f364b42ef1..f199f67520 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: retention-days: 1 - name: Bundle assets for signing if: inputs.configuration == 'Release' - run: ./build.sh -t Prepare-SignPath --configuration=${{ inputs.configuration }} --exclusive + run: ./build.sh Prepare-SignPath --configuration=${{ inputs.configuration }} --exclusive - name: Upload unsigned artifact id: upload-unsigned-artifact if: inputs.configuration == 'Release' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8c94126dd6..8d18277df0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -104,7 +104,7 @@ jobs: name: Release-repack-unsigned path: _build/repack/ - name: Build dmg - run: ./build.sh -t osx --configuration=Release --exclusive + run: ./build.sh osx --configuration=Release --exclusive - name: Push dmg to S3 run: aws s3 cp _build/osx/CKAN.dmg s3://${AWS_S3_BUCKET} --follow-symlinks @@ -137,7 +137,7 @@ jobs: - name: Build deb env: CODENAME: nightly - run: ./build.sh -t deb --configuration=Release --exclusive + run: ./build.sh deb --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -149,7 +149,7 @@ jobs: env: CODENAME: nightly DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build.sh -t deb-sign --configuration=Release --exclusive + run: ./build.sh deb-sign --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push deb to S3 run: aws s3 sync _build/deb/apt-repo-root s3://${AWS_S3_BUCKET}/deb --follow-symlinks @@ -194,7 +194,7 @@ jobs: mkdir -p _build/repack/Release cp _build/signed/ckan.exe _build/repack/Release - name: Build rpm - run: ./build.sh -t rpm --configuration=Release --exclusive + run: ./build.sh rpm --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -206,7 +206,7 @@ jobs: env: CODENAME: nightly DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build.sh -t rpm-repo --configuration=Release --exclusive + run: ./build.sh rpm-repo --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push nightly PRM repo to S3 run: aws s3 sync _build/rpm/repo s3://${AWS_S3_BUCKET}/rpm/nightly --follow-symlinks diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8fde395a9..b30801a0cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: name: Release-repack-unsigned path: _build/repack/ - name: Build dmg - run: ./build.sh -t osx --configuration=Release --exclusive + run: ./build.sh osx --configuration=Release --exclusive - name: Upload OSX release asset run: gh release upload ${{ github.event.release.tag_name }} _build/osx/CKAN.dmg env: @@ -79,7 +79,7 @@ jobs: - name: Build deb env: CODENAME: stable - run: ./build.sh -t deb --configuration=Release --exclusive + run: ./build.sh deb --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -91,7 +91,7 @@ jobs: env: CODENAME: stable DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build.sh -t deb-sign --configuration=Release --exclusive + run: ./build.sh deb-sign --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push deb to S3 run: aws s3 sync _build/deb/apt-repo-root s3://${AWS_S3_BUCKET}/deb --follow-symlinks @@ -130,7 +130,7 @@ jobs: VERSION=$(echo "${{ github.event.release.tag_name }}" | tr -d v) echo "RPM_VERSION=${VERSION}.$(date +'%g%j')" >> $GITHUB_ENV - name: Build rpm - run: ./build.sh -t rpm --configuration=Release --exclusive + run: ./build.sh rpm --configuration=Release --exclusive - name: Import GPG key env: DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} @@ -141,7 +141,7 @@ jobs: env: CODENAME: stable DEBIAN_PRIVATE_KEY: ${{ secrets.DEBIAN_PRIVATE_KEY }} - run: ./build.sh -t rpm-repo --configuration=Release --exclusive + run: ./build.sh rpm-repo --configuration=Release --exclusive if: ${{ env.DEBIAN_PRIVATE_KEY }} - name: Push stable RPM repo to S3 run: aws s3 sync _build/rpm/repo s3://${AWS_S3_BUCKET}/rpm/stable --follow-symlinks diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7761a05f7a..8e558f359b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: name: Debug-repack-unsigned path: _build/repack/ - name: Run tests - run: xvfb-run ./build.sh -t test+only --configuration=Debug --where="Category!=FlakyNetwork" + run: xvfb-run ./build.sh test+only --configuration=Debug --where="Category!=FlakyNetwork" # notify: # needs: diff --git a/build.ps1 b/build.ps1 index a7077190ae..e88c3d240d 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,2 +1,38 @@ -dotnet run --project build/Build.csproj -- $args -exit $LASTEXITCODE; \ No newline at end of file +Param ( + [Parameter(Position = 0)] + [string]$Arg0, + + [Parameter(ValueFromRemainingArguments = $true)] + [Object[]]$RemainingArgs +) + +# PSScriptRoot isn't set in PowerShell 2 +$minPSVer = [version]"3.0" +if (($PSVersionTable.PSVersion -lt $minPSVer)) { + [Console]::ForegroundColor = 'red' + [Console]::Error.WriteLine("This script does not support PowerShell $($PSVersionTable.PSVersion).") + [Console]::Error.WriteLine("Please upgrade to PowerShell $minPSVer or later.") + [Console]::ResetColor() + exit +} + +# Globals +$RootDir = "${PSScriptRoot}" +$ScriptFile = "${RootDir}/build/Build.csproj" +$BuildDir = "${RootDir}/_build" +$ToolsDir = "${BuildDir}/tools" + +# Build args +$cakeArgs = @() + +if ($Arg0) { + if ($Arg0[0] -eq "-") { + $cakeArgs += "${Arg0}" + } else { + $cakeArgs += "--target=${Arg0}" + } +} + +# Run Cake +dotnet run --project "${ScriptFile}" -- ${cakeArgs} ${RemainingArgs} +exit $LASTEXITCODE \ No newline at end of file diff --git a/build.sh b/build.sh index b4782143f3..9d944e5286 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,41 @@ #!/bin/sh +set -e -dotnet run --project ./build/Build.csproj -- "$@" +arg0="" +remainingArgs="" + +if [ $# -gt 0 ]; then + arg0="$1" +fi + +if [ $# -gt 1 ]; then + skippedFirst=false + for i in "$@"; do + if $skippedFirst; then + remainingArgs="$remainingArgs $i" + else + skippedFirst=true + fi + done +fi + +rootDir=$(dirname "$0") +scriptFile="$rootDir/build/Build.csproj" + +cakeArgs="" + +if [ -n "$arg0" ]; then + case $arg0 in + -*) + cakeArgs="$cakeArgs $arg0" + ;; + *) + cakeArgs="$cakeArgs --target=$arg0" + ;; + esac +fi + +export PATH="$PATH:$HOME/.dotnet/tools" +# shellcheck disable=SC2086 +dotnet run --project "$scriptFile" -- $cakeArgs $remainingArgs +exit $? diff --git a/doc/building.md b/doc/building.md index 55d25289fd..f159186a02 100644 --- a/doc/building.md +++ b/doc/building.md @@ -80,7 +80,7 @@ After Cake is installed by NuGet, the bootstrapper scripts then parse the comman The bootstrapper scripts use the following command line: ```plain -./build.sh [-t ] [...] +./build.sh [] [...] ``` Which is then used to execute Cake with the following command line: From a005fa5ba34769170a55157f9df5132e1482d8b7 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Sun, 23 Feb 2025 16:15:23 -0600 Subject: [PATCH 14/16] One last lingering -t --- doc/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/building.md b/doc/building.md index f159186a02..7b27bc4c10 100644 --- a/doc/building.md +++ b/doc/building.md @@ -184,7 +184,7 @@ push. GitHub's operation is pretty simple: - A bunch of packages are installed to make the build work. - Some commands are executed to simulate a graphical environment for testing. - The solution is built using `./build.sh --configuration=$BUILD_CONFIGURATION` -- The solution is tested using `./build.sh -t test+only --configuration=$BUILD_CONFIGURATION --where="Category!=FlakyNetwork"` +- The solution is tested using `./build.sh test+only --configuration=$BUILD_CONFIGURATION --where="Category!=FlakyNetwork"` - The `--where="Category!=FlakyNetwork"` argument is used to not execute tests that rely upon a network connection and could thus behave nondeterministically (frustrating behavior for a CI system). - The built executables (`ckan.exe` and `netkan.exe`) are uploaded to Amazon S3 if the following conditions are met: From 67b3331638333718c745f5fa8787be4d1cf22451 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Sun, 23 Feb 2025 18:00:22 -0600 Subject: [PATCH 15/16] Fix link --- doc/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/building.md b/doc/building.md index 7b27bc4c10..56ad870ad7 100644 --- a/doc/building.md +++ b/doc/building.md @@ -32,7 +32,7 @@ The following are the minimum requirements to build CKAN and NetKAN from source: - PowerShell - MSBuild (usually by installing [Visual Studio](https://www.visualstudio.com/vs/community/)) - [.NET Framework >= 4.8.0](https://dotnet.microsoft.com/en-us/download/dotnet-framework) -- [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) +- [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) ## Build directory From 3833b03bb45e4a0b7900c1a0bb59fc900ba0a2ed Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Sun, 23 Feb 2025 18:11:33 -0600 Subject: [PATCH 16/16] Remove stuff we don't need from .gitignore --- .gitignore | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/.gitignore b/.gitignore index 50f02605c9..ce61512ae9 100644 --- a/.gitignore +++ b/.gitignore @@ -59,9 +59,6 @@ quotes.txt.dat .AppleDouble .LSOverride -# Icon must end with two \r -Icon - # Thumbnails ._* @@ -107,56 +104,17 @@ Temporary Items .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - # mpeltonen/sbt-idea plugin .idea_modules/ -# JIRA plugin -atlassian-ide-plugin.xml - # Cursive Clojure plugin .idea/replstate.xml # SonarLint plugin .idea/sonarlint/ -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -