private static void Publish() { if (Directory.Exists(BinDistDirectory)) { IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {BinDistDirectory}"); } StartProcessAndWaitForExit("cmd", DesktopProjectDirectory, "dotnet clean --configuration Release && exit"); var desktopBinReleaseDirectory = Path.GetFullPath(Path.Combine(DesktopProjectDirectory, "bin", "Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release")); if (Directory.Exists(desktopBinReleaseDirectory)) { IoHelpers.TryDeleteDirectoryAsync(desktopBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {desktopBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } var deterministicFileNameTag = IsContinuousDelivery ? $"{DateTimeOffset.UtcNow:ddMMyyyy}{DateTimeOffset.UtcNow.TimeOfDay.TotalSeconds}" : VersionPrefix; var deliveryPath = IsContinuousDelivery ? Path.Combine(BinDistDirectory, "cdelivery") : BinDistDirectory; IoHelpers.EnsureDirectoryExists(deliveryPath); Console.WriteLine($"Binaries will be delivered here: {deliveryPath}"); foreach (string target in Targets) { string publishedFolder = Path.Combine(BinDistDirectory, target); string currentBinDistDirectory = publishedFolder; Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean"); // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21 // -c|--configuration {Debug|Release} // Defines the build configuration. The default value is Debug. // --force // Forces all dependencies to be resolved even if the last restore was successful. Specifying this flag is the same as deleting the project.assets.json file. // -o|--output <OUTPUT_DIRECTORY> // Specifies the path for the output directory. // If not specified, it defaults to ./bin/[configuration]/[framework]/publish/ for a framework-dependent deployment or // ./bin/[configuration]/[framework]/[runtime]/publish/ for a self-contained deployment. // If the path is relative, the output directory generated is relative to the project file location, not to the current working directory. // --self-contained // Publishes the .NET Core runtime with your application so the runtime does not need to be installed on the target machine. // If a runtime identifier is specified, its default value is true. For more information about the different deployment types, see .NET Core application deployment. // -r|--runtime <RUNTIME_IDENTIFIER> // Publishes the application for a given runtime. This is used when creating a self-contained deployment (SCD). // For a list of Runtime Identifiers (RIDs), see the RID catalog. Default is to publish a framework-dependent deployment (FDD). // --version-suffix <VERSION_SUFFIX> // Defines the version suffix to replace the asterisk (*) in the version field of the project file. // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x // --disable-parallel // Disables restoring multiple projects in parallel. // --no-cache // Specifies to not cache packages and HTTP requests. // https://github.com/dotnet/docs/issues/7568 // /p:Version=1.2.3.4 // "dotnet publish" supports msbuild command line options like /p:Version=1.2.3.4 string dotnetProcessArgs = string.Join( " ", $"publish", $"--configuration Release", $"--force", $"--output \"{currentBinDistDirectory}\"", $"--self-contained true", $"--runtime \"{target}\"", $"--disable-parallel", $"--no-cache", $"/p:VersionPrefix={VersionPrefix}", $"/p:DebugType=none", $"/p:DebugSymbols=false", $"/p:ErrorReport=none", $"/p:DocumentationFile=\"\"", $"/p:Deterministic=true", $"/p:RestoreLockedMode=true"); StartProcessAndWaitForExit( "dotnet", DesktopProjectDirectory, arguments: dotnetProcessArgs, redirectStandardOutput: true); Tools.ClearSha512Tags(currentBinDistDirectory); // Remove Tor binaries that are not relevant to the platform. var toNotRemove = ""; if (target.StartsWith("win")) { toNotRemove = "win"; } else if (target.StartsWith("linux")) { toNotRemove = "lin"; } else if (target.StartsWith("osx")) { toNotRemove = "osx"; } // Remove binaries that are not relevant to the platform. var binaryFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "Microservices", "Binaries")); foreach (var dir in binaryFolder.EnumerateDirectories()) { if (!dir.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase)) { IoHelpers.TryDeleteDirectoryAsync(dir.FullName).GetAwaiter().GetResult(); } } // Rename the final exe. string oldExecutablePath; string newExecutablePath; if (target.StartsWith("win")) { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop.exe"); newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe"); // Delete unused executables. File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.exe")); } else // Linux & OSX { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop"); newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName); // Delete unused executables. File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent")); } File.Move(oldExecutablePath, newExecutablePath); long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000; if (target.StartsWith("win")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; // In Windows build at this moment it does not matter though. } ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip")); if (IsContinuousDelivery) { continue; } } else if (target.StartsWith("osx")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip")); if (IsContinuousDelivery) { continue; } ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(BinDistDirectory, $"Wasabi-osx-{VersionPrefix}.zip")); IoHelpers.TryDeleteDirectoryAsync(currentBinDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {currentBinDistDirectory}"); } else if (target.StartsWith("linux")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip")); if (IsContinuousDelivery) { continue; } Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} does not exist."); } var newFolderName = $"Wasabi-{VersionPrefix}"; var newFolderPath = Path.Combine(BinDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var driveLetterUpper = BinDistDirectory[0]; var driveLetterLower = char.ToLower(driveLetterUpper); var linuxPath = $"/mnt/{driveLetterLower}/{Tools.LinuxPath(BinDistDirectory[3..])}";
private static void Publish() { if (Directory.Exists(BinDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(BinDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {BinDistDirectory}"); } using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = SolutionDirectory })) { process.StandardInput.WriteLine("git submodule update --init --recursive && exit"); process.WaitForExit(); } using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = GuiProjectDirectory })) { process.StandardInput.WriteLine("dotnet clean --configuration Release && exit"); process.WaitForExit(); } var guiBinReleaseDirectory = Path.GetFullPath(Path.Combine(GuiProjectDirectory, "bin\\Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin\\Release")); if (Directory.Exists(guiBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } foreach (string target in Targets) { string publishedFolder = Path.Combine(BinDistDirectory, target); string currentBinDistDirectory = publishedFolder; Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } using (var process = Process.Start(new ProcessStartInfo { FileName = "dotnet", Arguments = $"clean", WorkingDirectory = GuiProjectDirectory })) { process.WaitForExit(); } // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21 // -c|--configuration {Debug|Release} // Defines the build configuration. The default value is Debug. // --force // Forces all dependencies to be resolved even if the last restore was successful. Specifying this flag is the same as deleting the project.assets.json file. // -o|--output <OUTPUT_DIRECTORY> // Specifies the path for the output directory. // If not specified, it defaults to ./bin/[configuration]/[framework]/publish/ for a framework-dependent deployment or // ./bin/[configuration]/[framework]/[runtime]/publish/ for a self-contained deployment. // If the path is relative, the output directory generated is relative to the project file location, not to the current working directory. // --self-contained // Publishes the .NET Core runtime with your application so the runtime doesn't need to be installed on the target machine. // If a runtime identifier is specified, its default value is true. For more information about the different deployment types, see .NET Core application deployment. // -r|--runtime <RUNTIME_IDENTIFIER> // Publishes the application for a given runtime. This is used when creating a self-contained deployment (SCD). // For a list of Runtime Identifiers (RIDs), see the RID catalog. Default is to publish a framework-dependent deployment (FDD). // --version-suffix <VERSION_SUFFIX> // Defines the version suffix to replace the asterisk (*) in the version field of the project file. // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x // --disable-parallel // Disables restoring multiple projects in parallel. // --no-cache // Specifies to not cache packages and HTTP requests. // https://github.com/dotnet/docs/issues/7568 // /p:Version=1.2.3.4 // "dotnet publish" supports msbuild command line options like /p:Version=1.2.3.4 using (var process = Process.Start(new ProcessStartInfo { FileName = "dotnet", Arguments = $"publish --configuration Release --force --output \"{currentBinDistDirectory}\" --self-contained true --runtime \"{target}\" /p:VersionPrefix={VersionPrefix} --disable-parallel --no-cache /p:DebugType=none /p:DebugSymbols=false /p:ErrorReport=none /p:DocumentationFile=\"\" /p:Deterministic=true", WorkingDirectory = GuiProjectDirectory })) { process.WaitForExit(); } Tools.ClearSha512Tags(currentBinDistDirectory); Tools.RemoveSosDocsUnix(currentBinDistDirectory); // Remove Tor binaries those are not relevant to the platform. var torFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "TorDaemons")); var toNotremove = ""; if (target.StartsWith("win")) { toNotremove = "win"; } else if (target.StartsWith("linux")) { toNotremove = "linux"; } else if (target.StartsWith("osx")) { toNotremove = "osx"; } foreach (var file in torFolder.EnumerateFiles()) { if (!file.Name.Contains("data", StringComparison.OrdinalIgnoreCase) && !file.Name.Contains(toNotremove, StringComparison.OrdinalIgnoreCase)) { File.Delete(file.FullName); } } // Rename the final exe. string oldExecutablePath; string newExecutablePath; if (target.StartsWith("win")) { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui.exe"); newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe"); } else // Linux & OSX { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui"); newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName); } File.Move(oldExecutablePath, newExecutablePath); long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000; if (target.StartsWith("win")) { // Don't open console. if (!NSubsysUtil.ProcessFile(newExecutablePath)) { Console.WriteLine("ERROR: Couldn't remove console from exe."); } var icoPath = Path.Combine(GuiProjectDirectory, "Assets", "WasabiLogo.ico"); using (var process = Process.Start(new ProcessStartInfo { FileName = "rcedit", // https://github.com/electron/rcedit/ Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{VersionPrefix}\" --set-product-version \"{VersionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory })) { process.WaitForExit(); } // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; // In Windows build at this moment it doesn't matter though. } } else if (target.StartsWith("osx")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } var tempName = Path.Combine(BinDistDirectory, $"temp-{target}"); Directory.Move(currentBinDistDirectory, tempName); currentBinDistDirectory = tempName; string macWasabiAppDir = Path.Combine(publishedFolder, "Wasabi Wallet.App"); // This should be lowercase .app, but MAC will prevent people from upgrading if we change it. string macContentsDir = Path.Combine(macWasabiAppDir, "Contents"); string newName = Path.GetFullPath(Path.Combine(macContentsDir, "MacOS")); Directory.CreateDirectory(macContentsDir); Directory.Move(currentBinDistDirectory, newName); currentBinDistDirectory = newName; string resourcesDir = Path.Combine(macContentsDir, "Resources"); string infoFilePath = Path.Combine(macContentsDir, "Info.plist"); Directory.CreateDirectory(resourcesDir); var iconpath = Path.Combine(GuiProjectDirectory, "Assets", "WasabiLogo.icns"); File.Copy(iconpath, Path.Combine(resourcesDir, "WasabiLogo.icns")); string infoContent = $@"<?xml version=""1.0"" encoding=""UTF-8""?> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <plist version = ""1.0""> <dict> <key>LSMinimumSystemVersion</key> <string>10.12</string> <key>LSArchitecturePriority</key> <array> <string>x86_64</string> </array> <key>CFBundleIconFile</key> <string>WasabiLogo.icns</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>{VersionPrefix}</string> <key>CFBundleVersion</key> <string>{VersionPrefix}</string> <key>CFBundleExecutable</key> <string>{ExecutableName}</string> <key>CFBundleName</key> <string>Wasabi Wallet</string> <key>CFBundleIdentifier</key> <string>zksnacks.wasabiwallet</string> <key>NSHighResolutionCapable</key> <true/> <key>NSAppleScriptEnabled</key> <true/> <key>LSApplicationCategoryType</key> <string>public.app-category.finance</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> </dict> </plist> "; File.WriteAllText(infoFilePath, infoContent); using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = publishedFolder })) { process.StandardInput.WriteLine($"wsl ln -s /Applications && exit"); process.WaitForExit(); } //how to generate .DS_Store file - https://github.com/zkSNACKs/WalletWasabi/pull/928/commits/e38ed672dee25f6e45a3eb16584887cc6d48c4e6 var dmgContentDir = Path.Combine(PackagerProjectDirectory, "Content", "Osx"); IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentDir), new DirectoryInfo(publishedFolder)); string uncompressedDmgFileName = $"Wasabi-uncompressed.dmg"; string uncompressedDmgFilePath = Path.Combine(BinDistDirectory, uncompressedDmgFileName); string dmgFileName = $"Wasabi-{VersionPrefix}.dmg"; using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { // http://www.nathancoulson.com/proj_cross_tools.php // -D: Do not use deep directory relocation, and instead just pack them in the way we see them // -V: Volume Label // -no-pad: Do not pad the end by 150 sectors (300kb). As it is not a cd image, not required // -apple -r: Creates a .dmg image process.StandardInput.WriteLine($"wsl genisoimage -D -V \"Wasabi Wallet\" -no-pad -apple -r -dir-mode 755 -o \"{uncompressedDmgFileName}\" \"{new DirectoryInfo(publishedFolder).Name}\" && exit"); process.WaitForExit(); } // cd ~ // git clone https://github.com/planetbeing/libdmg-hfsplus.git && cd libdmg-hfsplus // https://github.com/planetbeing/libdmg-hfsplus/issues/14 // mkdir build && cd build // sudo apt-get install zlib1g-dev // cmake .. // cd build // sudo apt-get install libssl1.0-dev // cmake .. // cd ~/libdmg-hfsplus/build/ // make using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.StandardInput.WriteLine($"wsl ~/libdmg-hfsplus/build/dmg/./dmg dmg \"{uncompressedDmgFileName}\" \"{dmgFileName}\" && exit"); process.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); File.Delete(uncompressedDmgFilePath); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } else if (target.StartsWith("linux")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} doesn't exist."); } var newFolderName = $"WasabiLinux-{VersionPrefix}"; var newFolderPath = Path.Combine(BinDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.StandardInput.WriteLine($"wsl tar -pczvf {newFolderName}.tar.gz {newFolderName} && exit"); process.WaitForExit(); } Console.WriteLine("Create Linux .deb"); var debFolderRelativePath = "deb"; var debFolderPath = Path.Combine(BinDistDirectory, debFolderRelativePath); var linuxUsrLocalBinFolder = "/usr/local/bin/"; var debUsrLocalBinFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "local", "bin"); var debUsrLocalBinFolderPath = Path.Combine(BinDistDirectory, debUsrLocalBinFolderRelativePath); Directory.CreateDirectory(debUsrLocalBinFolderPath); var debUsrAppFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "applications"); var debUsrAppFolderPath = Path.Combine(BinDistDirectory, debUsrAppFolderRelativePath); Directory.CreateDirectory(debUsrAppFolderPath); var debUsrShareIconsFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "icons", "hicolor"); var debUsrShareIconsFolderPath = Path.Combine(BinDistDirectory, debUsrShareIconsFolderRelativePath); var debianFolderRelativePath = Path.Combine(debFolderRelativePath, "DEBIAN"); var debianFolderPath = Path.Combine(BinDistDirectory, debianFolderRelativePath); Directory.CreateDirectory(debianFolderPath); newFolderName = "wasabiwallet"; var linuxWasabiWalletFolder = Tools.LinuxPathCombine(linuxUsrLocalBinFolder, newFolderName); var newFolderRelativePath = Path.Combine(debUsrLocalBinFolderRelativePath, newFolderName); newFolderPath = Path.Combine(BinDistDirectory, newFolderRelativePath); Directory.Move(publishedFolder, newFolderPath); var assetsFolder = Path.Combine(GuiProjectDirectory, "Assets"); var assetsInfo = new DirectoryInfo(assetsFolder); foreach (var file in assetsInfo.EnumerateFiles()) { var number = file.Name.Split(new string[] { "WasabiLogo", ".png" }, StringSplitOptions.RemoveEmptyEntries); if (number.Count() == 1 && int.TryParse(number.First(), out int size)) { string destFolder = Path.Combine(debUsrShareIconsFolderPath, $"{size}x{size}", "apps"); Directory.CreateDirectory(destFolder); file.CopyTo(Path.Combine(destFolder, $"{ExecutableName}.png")); } } var controlFilePath = Path.Combine(debianFolderPath, "control"); // License format doesn't yet work, but should work in the future, it's work in progress: https://bugs.launchpad.net/ubuntu/+source/software-center/+bug/435183 var controlFileContent = $"Package: {ExecutableName}\n" + $"Priority: optional\n" + $"Section: utils\n" + $"Maintainer: nopara73 <*****@*****.**>\n" + $"Version: {VersionPrefix}\n" + $"Homepage: http://wasabiwallet.io\n" + $"Vcs-Git: git://github.com/zkSNACKs/WalletWasabi.git\n" + $"Vcs-Browser: https://github.com/zkSNACKs/WalletWasabi\n" + $"Architecture: amd64\n" + $"License: Open Source (MIT)\n" + $"Installed-Size: {installedSizeKb}\n" + $"Description: open-source, non-custodial, privacy focused Bitcoin wallet\n" + $" Built-in Tor, CoinJoin and Coin Control features.\n"; File.WriteAllText(controlFilePath, controlFileContent, Encoding.ASCII); var desktopFilePath = Path.Combine(debUsrAppFolderPath, $"{ExecutableName}.desktop"); var desktopFileContent = $"[Desktop Entry]\n" + $"Type=Application\n" + $"Name=Wasabi Wallet\n" + $"GenericName=Bitcoin Wallet\n" + $"Comment=Privacy focused Bitcoin wallet.\n" + $"Icon={ExecutableName}\n" + $"Terminal=false\n" + $"Exec={ExecutableName}\n" + $"Categories=Office;Finance;\n" + $"Keywords=bitcoin;wallet;crypto;blockchain;wasabi;privacy;anon;awesome;qwe;asd;\n"; File.WriteAllText(desktopFilePath, desktopFileContent, Encoding.ASCII); var wasabiStarterScriptPath = Path.Combine(debUsrLocalBinFolderPath, $"{ExecutableName}"); var wasabiStarterScriptContent = $"#!/bin/sh\n" + $"{ linuxWasabiWalletFolder.TrimEnd('/')}/./{ExecutableName}\n"; File.WriteAllText(wasabiStarterScriptPath, wasabiStarterScriptContent, Encoding.ASCII); string debExeLinuxPath = Tools.LinuxPathCombine(newFolderRelativePath, ExecutableName); string debDestopFileLinuxPath = Tools.LinuxPathCombine(debUsrAppFolderRelativePath, $"{ExecutableName}.desktop"); var wasabiStarterScriptLinuxPath = Tools.LinuxPathCombine(debUsrLocalBinFolderRelativePath, $"{ExecutableName}"); using (var process = Process.Start(new ProcessStartInfo { FileName = "wsl", Arguments = $"cd ~ && sudo umount /mnt/c && sudo mount -t drvfs C: /mnt/c -o metadata && cd /mnt/c/Users/user/Desktop/WalletWasabi/WalletWasabi.Gui/bin/dist && sudo chmod +x {debExeLinuxPath} && sudo chmod +x {wasabiStarterScriptLinuxPath} && sudo chmod -R 0644 {debDestopFileLinuxPath} && sudo chmod -R 0775 {Tools.LinuxPath(debianFolderRelativePath)} && dpkg --build {Tools.LinuxPath(debFolderRelativePath)} $(pwd)", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(debFolderPath).GetAwaiter().GetResult(); string oldDeb = Path.Combine(BinDistDirectory, $"{ExecutableName}_{VersionPrefix}_amd64.deb"); string newDeb = Path.Combine(BinDistDirectory, $"Wasabi-{VersionPrefix}.deb"); File.Move(oldDeb, newDeb); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } }
private static void Publish() { if (Directory.Exists(BinDistDirectory)) { IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {BinDistDirectory}"); } using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = GuiProjectDirectory })) { process.StandardInput.WriteLine("dotnet clean --configuration Release && exit"); process.WaitForExit(); } var guiBinReleaseDirectory = Path.GetFullPath(Path.Combine(GuiProjectDirectory, "bin", "Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release")); if (Directory.Exists(guiBinReleaseDirectory)) { IoHelpers.TryDeleteDirectoryAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } foreach (string target in Targets) { string publishedFolder = Path.Combine(BinDistDirectory, target); string currentBinDistDirectory = publishedFolder; Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } using (var process = Process.Start(new ProcessStartInfo { FileName = "dotnet", Arguments = $"clean", WorkingDirectory = GuiProjectDirectory })) { process.WaitForExit(); } // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21 // -c|--configuration {Debug|Release} // Defines the build configuration. The default value is Debug. // --force // Forces all dependencies to be resolved even if the last restore was successful. Specifying this flag is the same as deleting the project.assets.json file. // -o|--output <OUTPUT_DIRECTORY> // Specifies the path for the output directory. // If not specified, it defaults to ./bin/[configuration]/[framework]/publish/ for a framework-dependent deployment or // ./bin/[configuration]/[framework]/[runtime]/publish/ for a self-contained deployment. // If the path is relative, the output directory generated is relative to the project file location, not to the current working directory. // --self-contained // Publishes the .NET Core runtime with your application so the runtime does not need to be installed on the target machine. // If a runtime identifier is specified, its default value is true. For more information about the different deployment types, see .NET Core application deployment. // -r|--runtime <RUNTIME_IDENTIFIER> // Publishes the application for a given runtime. This is used when creating a self-contained deployment (SCD). // For a list of Runtime Identifiers (RIDs), see the RID catalog. Default is to publish a framework-dependent deployment (FDD). // --version-suffix <VERSION_SUFFIX> // Defines the version suffix to replace the asterisk (*) in the version field of the project file. // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x // --disable-parallel // Disables restoring multiple projects in parallel. // --no-cache // Specifies to not cache packages and HTTP requests. // https://github.com/dotnet/docs/issues/7568 // /p:Version=1.2.3.4 // "dotnet publish" supports msbuild command line options like /p:Version=1.2.3.4 using (var process = Process.Start(new ProcessStartInfo { FileName = "dotnet", Arguments = string.Join(" ", $"publish", $"--configuration Release", $"--force", $"--output \"{currentBinDistDirectory}\"", $"--self-contained true", $"--runtime \"{target}\"", $"--disable-parallel", $"--no-cache", $"/p:VersionPrefix={VersionPrefix}", $"/p:DebugType=none", $"/p:DebugSymbols=false", $"/p:ErrorReport=none", $"/p:DocumentationFile=\"\"", $"/p:Deterministic=true", $"/p:RestoreLockedMode=true"), WorkingDirectory = GuiProjectDirectory, RedirectStandardOutput = true })) { string error = process.StandardOutput.ReadToEnd(); process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException($"dotnet publish returned with error code {process.ExitCode}. Error message was: {error ?? "none"}"); } } Tools.ClearSha512Tags(currentBinDistDirectory); // Remove Tor binaries that are not relevant to the platform. var torFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "TorDaemons")); var toNotRemove = ""; if (target.StartsWith("win")) { toNotRemove = "win"; } else if (target.StartsWith("linux")) { toNotRemove = "lin"; } else if (target.StartsWith("osx")) { toNotRemove = "osx"; } foreach (var file in torFolder.EnumerateFiles()) { if (!file.Name.Contains("data", StringComparison.OrdinalIgnoreCase) && !file.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase)) { File.Delete(file.FullName); } } // Remove binaries that are not relevant to the platform. var binaryFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "Microservices", "Binaries")); foreach (var dir in binaryFolder.EnumerateDirectories()) { if (!dir.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase)) { IoHelpers.TryDeleteDirectoryAsync(dir.FullName).GetAwaiter().GetResult(); } } // Rename the final exe. string oldExecutablePath; string newExecutablePath; if (target.StartsWith("win")) { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui.exe"); newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe"); } else // Linux & OSX { oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui"); newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName); } File.Move(oldExecutablePath, newExecutablePath); long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000; if (target.StartsWith("win")) { var daemonExePath = newExecutablePath[0..^ 4] + "d.exe";