This commit is contained in:
Lucas Ontivero 2024-09-11 20:21:30 -03:00 коммит произвёл GitHub
Родитель b1ffed5355
Коммит ef8a5de9c8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
26 изменённых файлов: 9 добавлений и 2053 удалений

Просмотреть файл

@ -1,108 +1,16 @@
# Final tests
# Prepare the release
- Check the exact **date** of the last release and the **name** of the last PR.
- List the PR-s in order, open the [link and (adjust date!)](https://github.com/WalletWasabi/WalletWasabi/pulls?q=is%3Apr+merged%3A%3E%3D2019-07-07+sort%3Aupdated-asc).
- Go through all PR, create the Final Test issue. Create test cases according to PR-s and write a list - [Final Test format](https://github.com/WalletWasabi/WalletWasabi/issues/2227).
- Go through all issues and pick the [important ones (adjust date!)](https://github.com/WalletWasabi/WalletWasabi/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%3E%3D2019-07-07+sort%3Aupdated-asc+) and add to Final Tests if required.
- Check Tor status. Never release during a Tor network disruption: https://status.torproject.org/
- At the end there will be a Final Test document.
- Do testing contribution game if needed.
- Edit the `Contrib/ReleaseHighlight.md` file with the most important changes.
# Release candidate
# Release
- Go to your own fork of Wasabi and press Draft a new release. Release candidates are not published in the main repository!
- Tag version: add `rc1` postfix e.g: v1.1.7rc1.
- Set release title e.g: `Wasabi v1.1.7: <Release Title> - Release candidate`.
- Add "Do not use this" to the release notes.
- Set as a pre-release. Save Draft.
- Do Packaging (see below).
- Upload the files to the pre-release.
- Check `This is a pre-release` and press Publish Release.
- Add the pre-release link to the Final Test issue.
- Share the Final Test issue link with developers and test it for 24 hours.
- Every PR that is contained in the release must be at least 24 hours old.
Make sure to run a virus detection scan on one of the Release candidate's .msi installer (preferably the final one). You can use this site for example: https://www.virustotal.com/gui/home/upload.
# Packaging
- Make sure the local .NET Core version is up to date.
- Make sure CI and CodeFactor check out.
- Run tests.
- MAKE SURE YOU ARE ON THE RIGHT BRANCH AND UP TO DATE in GitHub Desktop on the release machine!
- Discard packages.lock changes if there are. Inserted USB drive name must contain the string USB!
- Run the [script file](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Packager/scripts/Wasabi_release.ps1) on the **Windows Release Laptop** and follow the instructions.
- At some point you will need to run [this script](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Packager/scripts/WasabiNoratize.scpt) file on Mac. Don't forget to open the script file on Mac and insert your Apple dev username and password. Guide how to setup it: [macOS release environment](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/Guides/MacOsSigning.md).
- Finish the script on Windows. Now a folder should pop up with all the files that need to be uploaded to GitHub.
- Test asc file for `.msi`.
- Final `.msi` test on own computer. Check the About dialog and optionally the BUILDINFO.json next to the wasabi executable, the commit ID should match with the one on GitHub.
# Final release
- Draft a [new release at the main repo](https://github.com/WalletWasabi/WalletWasabi/releases/new).
- Bump client version. (WalletWasabi/Helpers/Constants.cs) - maybe you already did this.
- Create the release notes by using [the template](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/ClientRelease/ReleaseNotesTemplate.md). Make sure the the recent changes are in the What's new section.
- Run tests.
- Do Packaging (see above).
- Upload the files to the main repo!
- Download MSI from the draft release to your local computer, test it, and verify the version number in about!
- Do not set pre-release flag!
- Publish the release.
# Notify
- Refresh website download and signature links.
- [Deploy testnet and mainnet backend](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/HowToDeploy.md). Make sure the client version number is bumped here as well. If it is a hotfix you do not need to update the backend, but you need to update the website!
- Make sure the commit to release builds.
- Tag the commit you want to release with format v0.0.0.0
- Push the tag to GitHub
- Test the packages
- Mark the release as final (remove the draft flag)
# Announce
- [Twitter](https://twitter.com) (tag @wasabiwallet #Bitcoin #Privacy).
- Submit to [/r/WasabiWallet](https://old.reddit.com/r/WasabiWallet/) and [/r/Bitcoin](https://old.reddit.com/r/Bitcoin/).
# Backporting
Backport is a branch. It is used for creating silent releases (hotfixes, small improvements) on top of the last released version. For this reason it has to be maintained with special care.
## Merge PR into backport (release branch)
- There is a PR which is merged to master and selected to backport.
- Checkout the current backport branch to a new local branch like bp_whatever: `git checkout -b bp_whatever upstream/backport`.
- Go to the merged PR / Commits and copy the hash of the commit.
- Cherry-pick: `git cherry-pick 35c4db348__hash of the commit__06abcd9278c`.
- git push origin bp_whatever.
- Create a PR into upstream/backport and name it as [Backport] Whatever.
## Code signing certificate
Digicert holds our Code Signing Certificate under the name "zkSNACKs Limited".
- Issuing CA: DigiCert SHA2 Assured ID Code Signing CA
- Platform: Microsoft Authenticode
- Type: Code Signing
- CSR Key Size: 3072
**Renewal**
- Create a new Certificate Signing Request (CSR) file with DigiCert® Certificate Utility application.
DigiCert® Certificate Utility is using the logged in user's public key to encrypt the file and only the same user can decrypt it after we receive the certificate.
Make sure to create the CSR file in David's profile (or wherever the release script is located)!
- Upload the CSR file to DigiCert.
- Wait for DigiCert to issue a new `zksnacks_limited.p7b` file.
- Import the `zksnacks_limited.p7b` file to DigiCert® Certificate Utility.
- Choose a friendly name for the certificate and apply the default password to it.
- Export the `zksnacks_limited.pfx` to `C:\zksnacks_limited.pfx`.
- Rename `C:\zksnacks_limited.pfx` to `C:\digicert.pfx`, so the Packager can find it!!
## Packager environment setup
### WSL
You can disable WSL sudo password prompt with this oneliner:
```
echo "`whoami` ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/`whoami` && sudo chmod 0440 /etc/sudoers.d/`whoami`
```
Use WSL 1 otherwise you cannot enter anything to the console (sudo password, appleid). https://github.com/microsoft/WSL/issues/4424

Просмотреть файл

@ -1,42 +0,0 @@
### _[Wasabi Wallet](https://WasabiWallet.io) is an easy to use, privacy-focused, open-source, non-custodial, Bitcoin wallet_
# Download
:window: [Windows](https://github.com/WalletWasabi/WalletWasabi/releases/download/v2.0.x/Wasabi-2.0.x.msi)
:green_apple: [Apple M1/M2](https://github.com/WalletWasabi/WalletWasabi/releases/download/v2.0.x/Wasabi-2.0.x-arm64.dmg)
:apple: [Apple Intel](https://github.com/WalletWasabi/WalletWasabi/releases/download/v2.0.x/Wasabi-2.0.x.dmg)
:penguin: [Ubuntu / Debian](https://github.com/WalletWasabi/WalletWasabi/releases/download/v2.0.x/Wasabi-2.0.x.deb)
:penguin: [Other Linux](https://github.com/WalletWasabi/WalletWasabi/releases/download/v2.0.x/Wasabi-2.0.x.tar.gz)
---
## Release Highlights
## Release Summary
Read the [related blog](https://blog.wasabiwallet.io/) for more information.
---
## Installation Guide
Download the operating system relevant software package and install Wasabi like you would with any other software on your computer.
For a detailed installation guide, including **signature verification**, see [the documentation](https://docs.wasabiwallet.io/using-wasabi/InstallPackage.html).
## Documentation
:spider_web: [Website](https://wasabiwallet.io)
:onion: [Tor onion site](http://wasabiukrxmkdgve5kynjztuovbg43uxcbcxn6y2okcrsg7gb6jdmbad.onion/)
:orange_book: [Documentation](https://docs.wasabiwallet.io)
:grey_question: [FAQ](https://github.com/WalletWasabi/WalletWasabi/discussions/categories/faq)
## Advanced Guide
If you want to build or update Wasabi from source code, check out [these easy instructions](https://docs.wasabiwallet.io/using-wasabi/BuildSource.html).
Wasabi uses [reproducible builds](https://reproducible-builds.org/), which you can verify with [this guide](https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/Guides/DeterministicBuildGuide.md).
## Requirements
- Windows 10 1607+
- Windows 11 22000+
- macOS 12.0+
- Ubuntu 22.04+
- Fedora 37+
- Debian 11+
---
## Full Changelog

Просмотреть файл

@ -1,64 +0,0 @@
using System.Linq;
namespace WalletWasabi.Packager;
/// <summary>
/// Class for processing program's command line arguments.
/// </summary>
public class ArgsProcessor
{
public ArgsProcessor(string[] args)
{
Args = args;
}
public string[] Args { get; }
/// <summary>Builds Wasabi Wallet binaries for supported platforms to be compared then with the official binaries, and terminates.</summary>
/// <seealso href="https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/Guides/DeterministicBuildGuide.md"/>
public bool IsOnlyBinariesMode() => IsOneOf("onlybinaries") || Args is null || Args.Length == 0;
public bool IsContinuousDeliveryMode() => IsOneOf("cdelivery");
public bool IsPublish() => IsOneOf("publish");
public bool IsSign() => IsOneOf("sign");
public bool IsGeneratePrivateKey() => IsOneOf("generatekeys");
private bool IsOneOf(params string[] values)
{
foreach (var value in values)
{
foreach (var arg in Args)
{
if (arg.Trim().TrimStart('-').Equals(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
public (string AppleId, string Password) GetAppleIdAndPassword()
{
string appleId = "";
string password = "";
try
{
var appleidArg = Args.First(a => a.Contains("appleid", StringComparison.InvariantCultureIgnoreCase));
var parameters = appleidArg.Split("=")[1];
var idAndPassword = parameters.Split(":");
appleId = idAndPassword[0];
password = idAndPassword[1];
}
catch (Exception)
{
}
return (appleId, password);
}
}

Просмотреть файл

@ -1,27 +0,0 @@
using System.Text.Json.Serialization;
namespace WalletWasabi.Packager;
/// <summary>
/// Contains information about environment and source code that was used to produce a Wasabi Wallet build.
/// </summary>
public class BuildInfo
{
[JsonConstructor]
public BuildInfo(string netRuntimeVersion, string netSdkVersion, string gitCommitHash)
{
NetRuntimeVersion = netRuntimeVersion;
NetSdkVersion = netSdkVersion;
GitCommitHash = gitCommitHash;
}
[JsonPropertyName("NetRuntimeVersion")]
public string NetRuntimeVersion { get; }
[JsonPropertyName("NetSdkVersion")]
public string NetSdkVersion { get; }
/// <summary>Git commit hash corresponding with the code that was used to produce a Wasabi Wallet build.</summary>
[JsonPropertyName("GitCommitHash")]
public string GitCommitHash { get; }
}

Просмотреть файл

@ -1,32 +0,0 @@
<?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>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>?</string>
<key>CFBundleVersion</key>
<string>?</string>
<key>CFBundleExecutable</key>
<string>wassabee</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>

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.0 KiB

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1 +0,0 @@
5D4F6D41-8967-4D1E-9953-35A263D5EFDF

Двоичный файл не отображается.

Просмотреть файл

@ -1,7 +0,0 @@
#!/usr/bin/env bash
key_chain="login.keychain"
key_chain_pass=""
security unlock-keychain -p "$key_chain_pass" "$key_chain"
security import macdevsign.p12 -k "$key_chain" -P "alma" -A
security set-key-partition-list -S apple-tool:,apple: -s -k "$key_chain_pass" "$key_chain"

Просмотреть файл

@ -1,14 +0,0 @@
<?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>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -1 +0,0 @@
global using System;

Просмотреть файл

@ -1,462 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using WalletWasabi.Helpers;
namespace WalletWasabi.Packager;
public static class MacSignTools
{
public static void Sign(ArgsProcessor argsProcessor)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
throw new NotSupportedException("This signing method is only valid on macOS!");
}
Console.WriteLine("Phase: finding the zip file on desktop which contains the compiled binaries from Windows.");
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string removableDriveFolder = Tools.GetSingleUsbDrive();
var srcZipFileNamePattern = "WasabiToNotarize-*";
var files = Directory.GetFiles(removableDriveFolder, srcZipFileNamePattern);
if (files.Length != 2)
{
throw new InvalidDataException($"{srcZipFileNamePattern} file missing or there are more! There must be exactly two!");
}
var (appleId, password) = argsProcessor.GetAppleIdAndPassword();
while (string.IsNullOrWhiteSpace(appleId))
{
Console.WriteLine("Enter appleId (email):");
appleId = Console.ReadLine();
}
while (string.IsNullOrWhiteSpace(password))
{
Console.WriteLine("Enter password:");
password = Console.ReadLine();
}
foreach (var zipPath in files)
{
var zipFile = Path.GetFileName(zipPath);
var versionPrefix = Path.GetFileNameWithoutExtension(zipPath).Split('-')[1]; // Example: "WasabiToNotarize-2.0.0.0-arm64.zip or WasabiToNotarize-2.0.0.0.zip ".
var workingDir = Path.Combine(desktopPath, "wasabiTemp");
var dmgPath = Path.Combine(workingDir, "dmg");
var unzippedPath = Path.Combine(workingDir, "unzipped");
var appName = $"{Constants.AppName}.app";
var appPath = Path.Combine(dmgPath, appName);
var appContentsPath = Path.Combine(appPath, "Contents");
var appMacOsPath = Path.Combine(appContentsPath, "MacOS");
var appResPath = Path.Combine(appContentsPath, "Resources");
var appFrameworksPath = Path.Combine(appContentsPath, "Frameworks");
var infoFilePath = Path.Combine(appContentsPath, "Info.plist");
var dmgFileName = zipFile.Replace("WasabiToNotarize", "Wasabi").Replace("zip", "dmg");
var dmgFilePath = Path.Combine(workingDir, dmgFileName);
var dmgUnzippedFilePath = Path.Combine(workingDir, $"Wasabi.tmp.dmg");
var appNotarizeFilePath = Path.Combine(workingDir, $"Wasabi-{versionPrefix}.zip");
var contentsPath = Path.GetFullPath(Path.Combine(Program.PackagerProjectDirectory.Replace("\\", "//"), "Content", "Osx"));
var entitlementsPath = Path.Combine(contentsPath, "entitlements.plist");
var dmgContentsDir = Path.Combine(contentsPath, "Dmg");
var desktopDmgFilePath = Path.Combine(desktopPath, dmgFileName);
var signArguments = $"--sign \"L233B2JQ68\" --verbose --force --options runtime --timestamp";
Console.WriteLine("Phase: creating the working directory.");
if (Directory.Exists(workingDir))
{
DeleteWithChmod(workingDir);
}
if (File.Exists(desktopDmgFilePath))
{
File.Delete(desktopDmgFilePath);
}
Console.WriteLine("Phase: creating the app.");
IoHelpers.EnsureDirectoryExists(appResPath);
IoHelpers.EnsureDirectoryExists(appMacOsPath);
ZipFile.ExtractToDirectory(zipPath, appMacOsPath); // Copy the binaries.
IoHelpers.CopyFilesRecursively(new DirectoryInfo(Path.Combine(contentsPath, "App")), new DirectoryInfo(appPath));
Console.WriteLine("Update the plist file with current information for example with version.");
var lines = File.ReadAllLines(infoFilePath);
string? bundleIdentifier = null;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
if (!line.TrimStart().StartsWith("<key>", StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
if (line.Contains("CFBundleShortVersionString", StringComparison.InvariantCulture) ||
line.Contains("CFBundleVersion", StringComparison.InvariantCulture))
{
lines[i + 1] = lines[i + 1].Replace("?", $"{Version.Parse(versionPrefix).ToString(3)}"); // Apple allow only 3 version tags in plist.
}
else if (line.Contains("CFBundleIdentifier", StringComparison.InvariantCulture))
{
bundleIdentifier = lines[i + 1].Trim().Replace("<string>", "").Replace("</string>", "");
}
}
if (string.IsNullOrWhiteSpace(bundleIdentifier))
{
throw new InvalidDataException("Bundle identifier not found in plist file.");
}
File.Delete(infoFilePath);
File.WriteAllLines(infoFilePath, lines);
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "chmod",
Arguments = $"-R u+rwX,go+rX,go-w \"{appPath}\"",
WorkingDirectory = workingDir
}))
{
WaitProcessToFinish(process, "chmod");
}
var filesToCheck = new[] { entitlementsPath };
foreach (var file in filesToCheck)
{
if (!File.Exists(file))
{
throw new FileNotFoundException($"File missing: {file}");
}
}
Console.WriteLine("Signing the files in app.");
IoHelpers.EnsureDirectoryExists(appResPath);
IoHelpers.EnsureDirectoryExists(appMacOsPath);
var executables = GetExecutables(appPath);
// The main executable needs to be signed last.
var filesToSignInOrder = Directory.GetFiles(appPath, "*.*", SearchOption.AllDirectories)
.OrderBy(file => executables.Contains(file))
.OrderBy(file => new FileInfo(file).Name == "wassabee")
.ToArray();
foreach (var file in executables)
{
using var process = Process.Start(new ProcessStartInfo
{
FileName = "chmod",
Arguments = $"u+x \"{file}\"",
WorkingDirectory = workingDir
});
WaitProcessToFinish(process, "chmod");
}
SignDirectory(filesToSignInOrder, workingDir, signArguments, entitlementsPath);
Console.WriteLine("Phase: verifying the signature.");
Verify(appPath);
Console.WriteLine("Phase: notarize the app.");
// Source: https://blog.frostwire.com/2019/08/27/apple-notarization-the-signature-of-the-binary-is-invalid-one-other-reason-not-explained-in-apple-developer-documentation/
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "ditto",
Arguments = $"-c -k --keepParent \"{appPath}\" \"{appNotarizeFilePath}\"",
WorkingDirectory = workingDir
}))
{
WaitProcessToFinish(process, "ditto");
}
Notarize(appleId, appNotarizeFilePath);
Staple(appPath);
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "spctl",
Arguments = $"-a -t exec -vv \"{appPath}\"",
WorkingDirectory = workingDir,
RedirectStandardError = true
}))
{
var nonNullProcess = WaitProcessToFinish(process, "spctl");
string result = nonNullProcess.StandardError.ReadToEnd();
if (!result.Contains(": accepted"))
{
throw new InvalidOperationException(result);
}
}
Console.WriteLine("Phase: creating the dmg.");
if (File.Exists(dmgFilePath))
{
File.Delete(dmgFilePath);
}
Console.WriteLine("Phase: creating dmg.");
IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentsDir), new DirectoryInfo(dmgPath));
File.Copy(Path.Combine(contentsPath, "WasabiLogo.icns"), Path.Combine(dmgPath, ".VolumeIcon.icns"), true);
var temp = Path.Combine(dmgPath, ".DS_Store.dat");
File.Move(temp, Path.Combine(dmgPath, ".DS_Store"), true);
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "ln",
Arguments = "-s /Applications",
WorkingDirectory = dmgPath
}))
{
WaitProcessToFinish(process, "ln");
}
var hdutilCreateArgs = string.Join(
" ",
new string[]
{
"create",
$"\"{dmgUnzippedFilePath}\"",
"-ov",
$"-volname \"Wasabi Wallet\"",
"-fs HFS+",
$"-srcfolder \"{dmgPath}\""
});
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "hdiutil",
Arguments = hdutilCreateArgs,
WorkingDirectory = dmgPath
}))
{
WaitProcessToFinish(process, "hdiutil");
}
var hdutilConvertArgs = string.Join(
" ",
new string[]
{
"convert",
$"\"{dmgUnzippedFilePath}\"",
"-format UDZO",
$"-o \"{dmgFilePath}\""
});
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "hdiutil",
Arguments = hdutilConvertArgs,
WorkingDirectory = dmgPath
}))
{
WaitProcessToFinish(process, "hdiutil");
}
Console.WriteLine("Phase: signing the dmg file.");
SignFile($"{signArguments} --entitlements \"{entitlementsPath}\" \"{dmgFilePath}\"", dmgPath);
Console.WriteLine("Phase: verifying the signature.");
Verify(dmgFilePath);
Console.WriteLine("Phase: notarize dmg");
Notarize(appleId, dmgFilePath);
Console.WriteLine("Phase: staple dmp");
Staple(dmgFilePath);
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "spctl",
Arguments = $"-a -t open --context context:primary-signature -v \"{dmgFilePath}\"",
WorkingDirectory = workingDir,
RedirectStandardError = true
}))
{
var nonNullProcess = WaitProcessToFinish(process, "spctl");
string result = nonNullProcess.StandardError.ReadToEnd();
if (!result.Contains(": accepted"))
{
throw new InvalidOperationException(result);
}
}
File.Move(dmgFilePath, desktopDmgFilePath);
DeleteWithChmod(workingDir);
Console.WriteLine($"Phase: finished for {dmgFileName}.");
var toRemovableFilePath = Path.Combine(removableDriveFolder, Path.GetFileName(desktopDmgFilePath));
File.Move(desktopDmgFilePath, toRemovableFilePath, true);
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}
}
Console.WriteLine("Phase: finished successfully.");
}
private static Process WaitProcessToFinish(Process? process, string processName)
{
if (process is null)
{
throw new InvalidOperationException($"Could not start ${processName} process.");
}
process.WaitForExit();
return process;
}
private static void Notarize(string appleId, string filePath)
{
Console.WriteLine("Start notarizing, uploading file.");
// -p WasabiNotarize = Saved the credentials in the keychain profile which keeps the password safe on the local machine. Name of the profile is "WasabiNotarize".
using var process = Process.Start(new ProcessStartInfo
{
FileName = "xcrun",
Arguments = $"notarytool submit --wait --apple-id \"{appleId}\" -p \"WasabiNotarize\" \"{filePath}\" ",
RedirectStandardOutput = true,
});
var nonNullProcess = WaitProcessToFinish(process, "xcrum");
string result = nonNullProcess.StandardOutput.ReadToEnd();
Console.WriteLine(result);
}
private static void Staple(string filePath)
{
using var process = Process.Start(new ProcessStartInfo
{
FileName = "xcrun",
Arguments = $"stapler staple \"{filePath}\"",
});
WaitProcessToFinish(process, "xcrum");
}
private static void DeleteWithChmod(string path)
{
using (var process = Process.Start(new ProcessStartInfo
{
FileName = "chmod",
Arguments = $"-R ugo+rwx \"{path}\"",
}))
{
WaitProcessToFinish(process, "chmod");
}
IoHelpers.TryDeleteDirectoryAsync(path).GetAwaiter().GetResult();
}
private static void SignFile(string arguments, string workingDir)
{
using var process = Process.Start(new ProcessStartInfo
{
FileName = "codesign",
Arguments = arguments,
WorkingDirectory = workingDir,
RedirectStandardError = true
});
var nonNullProcess = WaitProcessToFinish(process, "codesign");
var result = nonNullProcess.StandardError.ReadToEnd();
if (result.Contains("code object is not signed at all"))
{
throw new InvalidOperationException(result);
}
if (result.Contains("xcrun: error: invalid active developer path"))
{
throw new InvalidOperationException($"{result}\ntip: run xcode-select --install");
}
Console.WriteLine(result.Trim());
}
private static void Verify(string path)
{
using var process = Process.Start(new ProcessStartInfo
{
FileName = "codesign",
Arguments = $"-dv --verbose=4 \"{path}\"",
RedirectStandardError = true,
});
var nonNullProcess = WaitProcessToFinish(process, "codesign");
string result = nonNullProcess.StandardError.ReadToEnd();
if (!result.Contains("Authority=Developer ID Application: zkSNACKs Ltd."))
{
throw new InvalidOperationException(result);
}
}
private static void SignDirectory(string[] files, string workingDir, string signArguments, string entitlementsPath)
{
// Tor already signed by: The Tor Project, Inc (MADPSAYN6T)
// Wassabee has to be signed at the end. Otherwise codesign will throw a "submodule not signed" error.
foreach (var file in files)
{
var fileName = new FileInfo(file).Name;
if (fileName == ".DS_Store")
{
File.Delete(file);
continue;
}
SignFile($"{signArguments} --entitlements \"{entitlementsPath}\" \"{file}\"", workingDir);
}
}
private static IEnumerable<string> GetExecutables(string appPath)
{
string result = ExecuteBashCommand($"find -H \"{appPath}\" -print0 | xargs -0 file | grep \"Mach-O.* executable\"");
var lines = result.Split("\n").Where(x => !string.IsNullOrWhiteSpace(x));
var files = lines.Select(line => line.Split(":").First());
return files;
}
private static string ExecuteBashCommand(string command)
{
// according to: https://stackoverflow.com/a/15262019/637142
// Thanks to this we will pass everything as one command.
command = command.Replace("\"", "\"\"");
using var process = Process.Start(new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = $"-c \"{command}\"",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true
})
?? throw new InvalidOperationException("Could not start bash process.");
var result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
}

Просмотреть файл

@ -1,672 +0,0 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
using WalletWasabi.Userfacing;
namespace WalletWasabi.Packager;
/// <summary>
/// Instructions:
/// <list type="number">
/// <item>Bump Client version (or else wrong .msi will be created) - <see cref="Constants.ClientVersion"/>.</item>
/// <item>Publish with Packager.</item>
/// <item>Build WIX project with Release and x64 configuration.</item>
/// <item>Sign with Packager, set restore true so the password won't be kept.</item>
/// </list>
/// <seealso href="https://github.com/WalletWasabi/WalletWasabi/blob/master/WalletWasabi.Documentation/ClientDeployment.md"/>
/// </summary>
public static class Program
{
public const string PfxPath = "C:\\digicert.pfx";
public const string DaemonExecutableName = Constants.DaemonExecutableName;
public const string ExecutableName = Constants.ExecutableName;
private const string WasabiPrivateKeyFilePath = @"C:\wasabi\Wasabi.privkey";
private const string WasabiPublicKeyFilePath = @"C:\wasabi\Wasabi.pubkey";
/// <remarks>Only 64-bit platforms are supported for now.</remarks>
/// <seealso href="https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog"/>
private static string[] Targets = new[]
{
"win-x64",
"linux-x64",
"osx-x64",
"osx-arm64"
};
private static string VersionPrefix = Constants.ClientVersion.Revision == 0 ? Constants.ClientVersion.ToString(3) : Constants.ClientVersion.ToString();
private static bool OnlyBinaries;
private static bool IsContinuousDelivery;
public static string PackagerProjectDirectory { get; } = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", ".."));
public static string SolutionDirectory { get; } = Path.GetFullPath(Path.Combine(PackagerProjectDirectory, ".."));
public static string DesktopProjectDirectory { get; } = Path.GetFullPath(Path.Combine(SolutionDirectory, "WalletWasabi.Fluent.Desktop"));
public static string LibraryProjectDirectory { get; } = Path.GetFullPath(Path.Combine(SolutionDirectory, "WalletWasabi"));
public static string WixProjectDirectory { get; } = Path.GetFullPath(Path.Combine(SolutionDirectory, "WalletWasabi.WindowsInstaller"));
public static string BinDistDirectory { get; } = Path.GetFullPath(Path.Combine(DesktopProjectDirectory, "bin", "dist"));
/// <summary>
/// Main entry point.
/// </summary>
private static async Task Main(string[] args)
{
var argsProcessor = new ArgsProcessor(args);
// For now this is enough. If you run it on macOS you want to sign.
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
MacSignTools.Sign(argsProcessor);
return;
}
if (argsProcessor.IsGeneratePrivateKey())
{
await WasabiSignerHelpers.GeneratePrivateAndPublicKeyToFileAsync(WasabiPrivateKeyFilePath, WasabiPublicKeyFilePath).ConfigureAwait(false);
return;
}
// Only binaries mode is for deterministic builds.
OnlyBinaries = argsProcessor.IsOnlyBinariesMode();
IsContinuousDelivery = argsProcessor.IsContinuousDeliveryMode();
ReportStatus();
if (argsProcessor.IsPublish() || IsContinuousDelivery || OnlyBinaries)
{
await PublishAsync().ConfigureAwait(false);
IoHelpers.OpenFolderInFileExplorer(BinDistDirectory);
}
if (argsProcessor.IsSign())
{
await SignAsync().ConfigureAwait(false);
}
}
private static void ReportStatus()
{
if (OnlyBinaries)
{
Console.WriteLine("I'll only generate binaries and disregard all other options.");
}
Console.WriteLine($"{nameof(VersionPrefix)}:\t\t\t{VersionPrefix}");
Console.WriteLine($"{nameof(ExecutableName)}:\t\t\t{ExecutableName}");
Console.WriteLine();
Console.Write($"{nameof(Targets)}:\t\t\t");
foreach (var target in Targets)
{
if (Targets.Last() != target)
{
Console.Write($"{target}, ");
}
else
{
Console.Write(target);
}
}
Console.WriteLine();
}
private static async Task SignAsync()
{
foreach (string target in Targets)
{
if (target.StartsWith("win", StringComparison.OrdinalIgnoreCase))
{
string publishedFolder = Path.Combine(BinDistDirectory, target);
Console.WriteLine("Move created .msi");
var msiPath = Path.Combine(WixProjectDirectory, "bin", "Release", "Wasabi.msi");
var msiFileName = Path.GetFileNameWithoutExtension(msiPath);
var newMsiPath = Path.Combine(BinDistDirectory, $"{msiFileName}-{VersionPrefix}.msi");
if (File.Exists(newMsiPath))
{
Console.WriteLine("MSI file was already there, skipping code signing phase.");
continue;
}
if (!File.Exists(msiPath))
{
throw new Exception(".msi does not exist. Expected path: Wasabi.msi.");
}
File.Move(msiPath, newMsiPath);
Console.Write("Enter Code Signing Certificate Password: ");
string pfxPassword = PasswordConsole.ReadPassword();
// Sign code with digicert.
StartProcessAndWaitForExit("cmd", BinDistDirectory, $"signtool sign /d \"Wasabi Wallet\" /f \"{PfxPath}\" /p {pfxPassword} /t http://timestamp.digicert.com /a \"{newMsiPath}\" && exit");
await IoHelpers.TryDeleteDirectoryAsync(publishedFolder).ConfigureAwait(false);
Console.WriteLine($"Deleted {publishedFolder}");
}
else if (target.StartsWith("osx", StringComparison.OrdinalIgnoreCase))
{
string dmgFileName = target.Contains("arm") ? $"Wasabi-{VersionPrefix}.dmg" : $"Wasabi-{VersionPrefix}-arm64.dmg";
string destinationFilePath = Path.Combine(BinDistDirectory, dmgFileName);
if (File.Exists(destinationFilePath))
{
continue;
}
string dmgFilePath = Path.Combine(Tools.GetSingleUsbDrive(), dmgFileName);
if (!File.Exists(dmgFilePath))
{
throw new Exception(".dmg does not exist.");
}
File.Move(dmgFilePath, destinationFilePath);
}
}
Console.WriteLine("Signing final files...");
var finalFiles = Directory.GetFiles(BinDistDirectory);
var sha256SumsFilePath = Path.Combine(BinDistDirectory, "SHA256SUMS");
foreach (var finalFile in finalFiles)
{
StartProcessAndWaitForExit("cmd", BinDistDirectory, $"gpg --armor --detach-sign {finalFile} && exit");
StartProcessAndWaitForExit("cmd", WixProjectDirectory, $"git checkout -- ComponentsGenerated.wxs && exit");
ExecuteBashCommands(new[] { $"sha256sum {Path.GetFileName(finalFile)} >> SHA256SUMS" });
}
StartProcessAndWaitForExit("cmd", BinDistDirectory, $"gpg --sign --digest-algo sha256 -a --clearsign --armor --output SHA256SUMS.asc SHA256SUMS && exit");
// We do not need this file anymore SHA256SUMS.ASC contains the hashes and the signature as well.
File.Delete(sha256SumsFilePath);
using var key = await WasabiSignerHelpers.GetPrivateKeyFromFileAsync(WasabiPrivateKeyFilePath).ConfigureAwait(false);
// We will sign the whole file with the hashes and the pgp signature.
var sha256sumAscFilePath = Path.Combine(BinDistDirectory, "SHA256SUMS.asc");
await WasabiSignerHelpers.SignSha256SumsFileAsync(sha256sumAscFilePath, key).ConfigureAwait(false);
// Verify back the signature file.
await WasabiSignerHelpers.VerifySha256SumsFileAsync(sha256sumAscFilePath).ConfigureAwait(false);
// Verify back Wasabi installer's hashes
await WasabiSignerHelpers.VerifyInstallerFileHashesAsync(finalFiles, sha256sumAscFilePath).ConfigureAwait(false);
IoHelpers.OpenFolderInFileExplorer(BinDistDirectory);
}
private static async Task PublishAsync()
{
if (Directory.Exists(BinDistDirectory))
{
await IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).ConfigureAwait(false);
Console.WriteLine($"# Deleted {BinDistDirectory}");
}
Console.WriteLine($"# Run dotnet restore");
StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "restore --locked-mode");
Console.WriteLine($"# Run dotnet clean");
StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean --configuration Release");
string desktopBinReleaseDirectory = Path.GetFullPath(Path.Combine(DesktopProjectDirectory, "bin", "Release"));
string libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release"));
if (Directory.Exists(desktopBinReleaseDirectory))
{
await IoHelpers.TryDeleteDirectoryAsync(desktopBinReleaseDirectory).ConfigureAwait(false);
Console.WriteLine($"# Deleted {desktopBinReleaseDirectory}");
}
if (Directory.Exists(libraryBinReleaseDirectory))
{
await IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).ConfigureAwait(false);
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}");
string buildInfoJson = GetBuildInfoData();
CheckUncommittedGitChanges();
foreach (string target in Targets)
{
string publishedFolder = Path.Combine(BinDistDirectory, target);
string currentBinDistDirectory = publishedFolder;
Console.WriteLine();
Console.WriteLine($"# Packaging for platform '{target}' to folder:\t{currentBinDistDirectory}");
Console.WriteLine();
if (!Directory.Exists(currentBinDistDirectory))
{
Directory.CreateDirectory(currentBinDistDirectory);
Console.WriteLine($"# Created {currentBinDistDirectory}");
}
string buildInfoPath = Path.Combine(currentBinDistDirectory, "BUILDINFO.json");
File.WriteAllText(buildInfoPath, buildInfoJson);
StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean");
// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish for details.
string dotnetProcessArgs = string.Join(
" ",
$"publish",
$"--configuration Release",
$"--force",
$"--output \"{currentBinDistDirectory}\"",
$"--self-contained true",
$"--runtime \"{target}\"",
$"--disable-parallel",
$"--no-cache",
$"--no-restore",
$"/p:VersionPrefix={VersionPrefix}",
$"/p:DebugType=none",
$"/p:DebugSymbols=false",
$"/p:ErrorReport=none",
$"/p:DocumentationFile=\"\"",
$"/p:Deterministic=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))
{
await IoHelpers.TryDeleteDirectoryAsync(dir.FullName).ConfigureAwait(false);
}
}
// Rename WalletWasabi.Fluent.Desktop(.exe) -> wassabee(.exe).
string executableExtension = target.StartsWith("win") ? ".exe" : "";
string oldExecutablePath = Path.Combine(currentBinDistDirectory, $"WalletWasabi.Fluent.Desktop{executableExtension}");
string newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}{executableExtension}");
File.Move(oldExecutablePath, newExecutablePath);
// Rename WalletWasabi.Daemon(.exe) -> wassabeed(.exe).
oldExecutablePath = Path.Combine(currentBinDistDirectory, $"WalletWasabi.Daemon{executableExtension}");
newExecutablePath = Path.Combine(currentBinDistDirectory, $"{DaemonExecutableName}{executableExtension}");
File.Move(oldExecutablePath, newExecutablePath);
// Delete unused executables.
File.Delete(Path.Combine(currentBinDistDirectory, $"WalletWasabi.Fluent{executableExtension}"));
// IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
if (OnlyBinaries)
{
continue;
}
long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000;
if (target.StartsWith("win"))
{
ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(target)}.zip"));
if (IsContinuousDelivery)
{
continue;
}
}
else if (target.StartsWith("osx"))
{
ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(target)}.zip"));
if (IsContinuousDelivery)
{
continue;
}
// Only add postfix to the final package if arm64, otherwise nothing.
var postfix = target.Contains("arm64") ? "-arm64" : "";
// After notarization this will be the filename of the dmg file.
var zipFileName = $"WasabiToNotarize-{deterministicFileNameTag}{postfix}.zip";
var zipFilePath = Path.Combine(BinDistDirectory, zipFileName);
ZipFile.CreateFromDirectory(currentBinDistDirectory, zipFilePath);
await IoHelpers.TryDeleteDirectoryAsync(currentBinDistDirectory).ConfigureAwait(false);
Console.WriteLine($"# Deleted {currentBinDistDirectory}");
string drive = Tools.GetSingleUsbDrive();
string targetFilePath = Path.Combine(drive, zipFileName);
try
{
File.Move(zipFilePath, targetFilePath, overwrite: true);
Console.WriteLine($"# Moved '{zipFilePath}' unsigned zip file to the USB disk drive ('{targetFilePath}').");
}
catch (Exception ex)
{
Console.WriteLine($"# There was an error during moving '{zipFilePath}' file to the USB disk drive ('{targetFilePath}'): '{ex.Message}'. Ignoring.");
}
}
else if (target.StartsWith("linux"))
{
ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(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);
Console.WriteLine($"# Move '{publishedFolder}' to '{newFolderPath}'.");
Directory.Move(publishedFolder, newFolderPath);
publishedFolder = newFolderPath;
string chmodExecutablesArgs = $$"""-type f \( -name '{{ExecutableName}}' -o -name '{{DaemonExecutableName}}' -o -name 'hwi' -o -name 'bitcoind' -o -name 'tor' \) -exec chmod +x {} \;""";
string[] commands = new string[]
{
$"sudo find ./{newFolderName} -type f -exec chmod 644 {{}} \\;",
$"sudo find ./{newFolderName} {chmodExecutablesArgs}",
$"tar -pczvf {newFolderName}.tar.gz {newFolderName}",
};
ExecuteBashCommands(commands);
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(DesktopProjectDirectory, "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.Length == 1 && int.TryParse(number.First(), out int size))
{
string destinationFolder = Path.Combine(debUsrShareIconsFolderPath, $"{size}x{size}", "apps");
Directory.CreateDirectory(destinationFolder);
file.CopyTo(Path.Combine(destinationFolder, $"{ExecutableName}.png"));
}
}
var controlFilePath = Path.Combine(debianFolderPath, "control");
// License format does not 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: zkSNACKs Ltd <info@zksnacks.com>\n" +
$"Version: {VersionPrefix}\n" +
$"Homepage: https://wasabiwallet.io\n" +
$"Vcs-Git: git://github.com/WalletWasabi/WalletWasabi.git\n" +
$"Vcs-Browser: https://github.com/WalletWasabi/WalletWasabi\n" +
$"Architecture: amd64\n" +
$"License: Open Source (MIT)\n" +
$"Installed-Size: {installedSizeKb}\n" +
$"Recommends: policykit-1\n" +
$"Description: open-source, non-custodial, privacy focused Bitcoin wallet\n" +
$" Built-in Tor, coinjoin, payjoin and coin control features.\n";
File.WriteAllText(controlFilePath, controlFileContent, Encoding.ASCII);
string postInstScriptContent = """
#!/bin/sh
/usr/local/bin/wasabiwallet/Microservices/Binaries/lin64/hwi installudevrules
exit 0
""".ReplaceLineEndings("\n");
string postInstScriptPath = Path.Combine(debianFolderPath, "postinst");
File.WriteAllText(postInstScriptPath, postInstScriptContent, Encoding.ASCII);
var desktopFilePath = Path.Combine(debUsrAppFolderPath, $"{ExecutableName}.desktop");
var desktopFileContent = $"[Desktop Entry]\n" +
$"Type=Application\n" +
$"Name=Wasabi Wallet\n" +
$"StartupWMClass=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;\n";
File.WriteAllText(desktopFilePath, desktopFileContent, Encoding.ASCII);
const string Shebang = "#!/usr/bin/env sh\n";
var wasabiStarterScriptPath = Path.Combine(debUsrLocalBinFolderPath, $"{ExecutableName}");
var wasabiStarterScriptContent = Shebang +
$"{linuxWasabiWalletFolder.TrimEnd('/')}/{ExecutableName} $@\n";
var wasabiDaemonStarterScriptPath = Path.Combine(debUsrLocalBinFolderPath, $"{DaemonExecutableName}");
var wasabiDaemonStarterScriptContent = Shebang +
$"{linuxWasabiWalletFolder.TrimEnd('/')}/{DaemonExecutableName} $@\n";
File.WriteAllText(wasabiStarterScriptPath, wasabiStarterScriptContent, Encoding.ASCII);
File.WriteAllText(wasabiDaemonStarterScriptPath, wasabiDaemonStarterScriptContent, Encoding.ASCII);
string debDesktopFileLinuxPath = Tools.LinuxPathCombine(debUsrAppFolderRelativePath, $"{ExecutableName}.desktop");
commands = new string[]
{
$"sudo find {Tools.LinuxPath(newFolderRelativePath)} -type f -exec chmod 644 {{}} \\;",
$"sudo find {Tools.LinuxPath(newFolderRelativePath)} {chmodExecutablesArgs}",
$"sudo chmod -R 0775 {Tools.LinuxPath(debianFolderRelativePath)}",
$"sudo chmod -R 0644 {debDesktopFileLinuxPath}",
$"dpkg --build {Tools.LinuxPath(debFolderRelativePath)} $(pwd)"
};
ExecuteBashCommands(commands);
await IoHelpers.TryDeleteDirectoryAsync(debFolderPath).ConfigureAwait(false);
string oldDeb = Path.Combine(BinDistDirectory, $"{ExecutableName}_{VersionPrefix}_amd64.deb");
string newDeb = Path.Combine(BinDistDirectory, $"Wasabi-{VersionPrefix}.deb");
File.Move(oldDeb, newDeb);
await IoHelpers.TryDeleteDirectoryAsync(publishedFolder).ConfigureAwait(false);
Console.WriteLine($"# Deleted {publishedFolder}");
}
}
}
/// <summary>Checks whether there are uncommitted changes.</summary>
/// <remarks>This is important to really release a build that corresponds with a git hash.</remarks>
private static void CheckUncommittedGitChanges()
{
if (TryStartProcessAndWaitForExit("git", workingDirectory: SolutionDirectory, out var gitStatus, arguments: "status --porcelain", redirectStandardOutput: true) && !string.IsNullOrEmpty(gitStatus))
{
Console.WriteLine("BEWARE: There are uncommitted changes in the repository. Do you want to continue? (Y/N)");
int i = Console.Read();
char ch = Convert.ToChar(i);
if (ch != 'y' && ch != 'Y')
{
Console.WriteLine("\nExiting.");
Environment.Exit(1);
}
}
}
/// <summary>
/// Gets information about .NET SDK and .NET runtime so that deterministic build is easier to reproduce.
/// </summary>
/// <returns>JSON string to write to a <c>BUILDINFO.json</c> file.</returns>
private static string GetBuildInfoData()
{
// .NET runtime version. We rely on the fact that this version is the same as if we run "dotnet" command. This should be a safe assumption.
Version runtimeVersion = Environment.Version;
// Get .NET SDK version.
if (!TryStartProcessAndWaitForExit("dotnet", workingDirectory: SolutionDirectory, result: out var sdkVersion, arguments: "--version", redirectStandardOutput: true))
{
sdkVersion = "Failed to get .NET SDK version.";
}
// Get git commit ID.
if (!TryStartProcessAndWaitForExit("git", workingDirectory: SolutionDirectory, result: out var gitCommitId, arguments: "rev-parse HEAD", redirectStandardOutput: true))
{
gitCommitId = "Failed to get git commit ID.";
}
return JsonSerializer.Serialize(new BuildInfo(runtimeVersion.ToString(), sdkVersion, gitCommitId), new JsonSerializerOptions() { WriteIndented = true });
}
/// <summary>
/// Executes a set of commands in either WSL2 (on Windows) or Bash (on other platforms).
/// </summary>
/// <param name="commands">Commands to execute.</param>
private static void ExecuteBashCommands(string[] commands)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Use WSL on Windows.
string arguments = Tools.CreateWslCommand(BinDistDirectory, commands);
StartProcessAndWaitForExit("wsl", BinDistDirectory, arguments: arguments);
}
else
{
// Use Bash on other platforms.
string arguments = string.Join(" && ", commands);
StartProcessAndWaitForExit("bash", BinDistDirectory, arguments: $"-c \"{arguments}\"");
}
}
private static string? StartProcessAndWaitForExit(string command, string workingDirectory, string? writeToStandardInput = null, string? arguments = null, bool redirectStandardOutput = false)
{
var isWriteToStandardInput = !string.IsNullOrWhiteSpace(writeToStandardInput);
using var process = Process.Start(new ProcessStartInfo
{
FileName = command,
Arguments = arguments ?? "",
RedirectStandardInput = isWriteToStandardInput,
RedirectStandardOutput = redirectStandardOutput,
WorkingDirectory = workingDirectory
})
?? throw new InvalidOperationException($"Process '{command}' is invalid.");
if (isWriteToStandardInput)
{
process.StandardInput.WriteLine(writeToStandardInput);
}
string? output = null;
if (redirectStandardOutput)
{
output = process.StandardOutput.ReadToEnd();
}
process.WaitForExit();
if (process.ExitCode is not 0)
{
Console.WriteLine($"Process failed:");
Console.WriteLine($"* Command: '{command} {arguments}'");
Console.WriteLine($"* Working directory: '{workingDirectory}'");
Console.WriteLine($"* Exit code: '{process.ExitCode}'");
if (redirectStandardOutput)
{
string prettyPrint = string.Join(Environment.NewLine, (output ?? "").Split(Environment.NewLine).Select(line => $" > {line}"));
Console.WriteLine($"* Output:\n{prettyPrint}");
}
throw new InvalidOperationException("Process exited with unexpected exit code");
}
return output;
}
private static bool TryStartProcessAndWaitForExit(string command, string workingDirectory, [NotNullWhen(true)] out string? result, string? writeToStandardInput = null, string? arguments = null, bool redirectStandardOutput = false)
{
result = null;
try
{
result = StartProcessAndWaitForExit(command, workingDirectory, writeToStandardInput, arguments, redirectStandardOutput)?.Trim() ?? "";
return true;
}
catch (Exception ex)
{
Console.WriteLine($"# Process failed: '{ex}'.");
}
return false;
}
private static string GetPackageTargetPostfix(string target)
{
if (target.StartsWith("osx"))
{
return target.Replace("osx", "macOS");
}
return target;
}
}

Просмотреть файл

@ -1,121 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace WalletWasabi.Packager;
public static class Tools
{
public static void ClearSha512Tags(string pathToSearch)
{
var files = Directory.GetFiles(pathToSearch, "*.deps.json"); // https://natemcmaster.com/blog/2017/12/21/netcore-primitives/
if (files is null)
{
return;
}
foreach (var depsFilePath in files)
{
var lines = File.ReadAllLines(depsFilePath);
List<string> outLines = new();
foreach (var line in lines)
{
// "sha512": "sha512-B0BYh5Fpeqp4GIbL5wEhde6M/dZ+s0tlXM0qMTvj4mTg9Rr4svVHGpn6dDp8pT2sB88ghxyLIpKGdx9Oj7f/pw==",
if (line.Contains("\"sha512\": \"sha512-"))
{
// "sha512": "",
var lineToAdd = " \"sha512\": \"\"";
if (line.EndsWith(','))
{
lineToAdd += ',';
}
outLines.Add(lineToAdd);
}
else
{
outLines.Add(line);
}
}
File.Delete(depsFilePath);
File.WriteAllLines(depsFilePath, outLines);
}
}
public static string LinuxPathCombine(params string[] paths)
{
return LinuxPath(Path.Combine(paths));
}
public static string LinuxPath(string path)
{
return path.Replace(@"\", @"/");
}
/// <summary>Converts a Windows path to a WSL path.</summary>
/// <param name="windowsPath">Full Windows path (e.g. <c>C:\something\something</c>).</param>
/// <param name="driveLetterUpper">Output parameter with capital drive letter of <paramref name="windowsPath"/>.</param>
/// <returns>WSL path corresponding with <paramref name="windowsPath"/> (e.g. <c>/mnt/c/something/something</c>).</returns>
/// <remarks>Supports both WSL 1 and WSL 2.</remarks>
public static string Win2WslPath(string windowsPath, out char driveLetterUpper)
{
driveLetterUpper = char.ToUpper(windowsPath[0]);
return $"/mnt/{char.ToLower(driveLetterUpper)}/{LinuxPath(windowsPath[3..])}";
}
/// <summary>Builds a WSL command from <paramref name="commands"/> in a way that <c>chmod</c> command works in WSL (not true by default).</summary>
/// <param name="windowsWorkingDirectory">Working directory where to run <paramref name="commands"/> as a Windows full path.</param>
/// <param name="commands">Linux commands to execute in WSL folder corresponding to <paramref name="windowsWorkingDirectory"/>.</param>
public static string CreateWslCommand(string windowsWorkingDirectory, params string[] commands)
{
string wslPath = Win2WslPath(windowsWorkingDirectory, out char driveLetterUpper);
char driveLetterLower = char.ToLower(driveLetterUpper);
string[] allCommands = new string[]
{
$"cd /",
$"sudo umount -l /mnt/{driveLetterLower}",
$"sudo mount -t drvfs {driveLetterUpper}: /mnt/{driveLetterLower} -o metadata",
$"cd {wslPath}",
}.Concat(commands).ToArray();
return string.Join(" && ", allCommands);
}
public static long DirSize(DirectoryInfo d)
{
long size = 0;
// Add file sizes.
FileInfo[] fis = d.GetFiles();
foreach (FileInfo fi in fis)
{
size += fi.Length;
}
// Add subdirectory sizes.
DirectoryInfo[] dis = d.GetDirectories();
foreach (DirectoryInfo di in dis)
{
size += DirSize(di);
}
return size;
}
public static string GetSingleUsbDrive()
{
IEnumerable<DriveInfo> driveList;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
driveList = DriveInfo.GetDrives().Where(d => d.Name.Contains("USB")).ToArray();
}
else
{
driveList = DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Removable);
}
return driveList.Single().Name;
}
}

Просмотреть файл

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
<NoWarn>1701;1702;1705;1591;1573;CA1031;CA1822</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64;osx-arm64;</RuntimeIdentifiers>
<PathMap>$(MSBuildProjectDirectory)\=WalletWasabi.Packager</PathMap>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\Debug\net8.0\WalletWasabi.Packager.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile></DocumentationFile>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<ErrorReport>none</ErrorReport>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\WalletWasabi\WalletWasabi.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,422 +0,0 @@
{
"version": 2,
"dependencies": {
"net8.0": {
"Microsoft.CodeAnalysis.BannedApiAnalyzers": {
"type": "Direct",
"requested": "[3.3.4, )",
"resolved": "3.3.4",
"contentHash": "0k2Jwpc8eq0hjOtX6TxRkHm9clkJ2PAQ3heEHgqIJZcsfdFosC/iyz18nsgTVDDWpID80rC7aiYK7ripx+Qndg=="
},
"LinqKit.Core": {
"type": "Transitive",
"resolved": "1.2.5",
"contentHash": "+5UKyagtVX33TSeOGorYzDXus/ypAISfM7HFfrix4BEmuuGF+nhSjf8P9csejLQ79ny5ms33M5/lQ2SkwC0Jxw=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "pujbzfszX7jAl7oTbHhqx7pxd9jibeyHHl8zy1gd55XMaKWjDtc5XhhNYwQnrwWYCInNdVoArbaaAvLgW7TwuA==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "8.0.0",
"Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"System.Diagnostics.DiagnosticSource": "8.0.0"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.Configuration.Binder": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
},
"Microsoft.Net.Http.Headers": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "YlHqL8oWBX3H1LmdKUOxEMW8cVD8nUACEnE2Fu3Ze4k7mYf8yJ1o/uLqoequQV0GDupXyCBEzYhn7Zxdz7pqYQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"NBitcoin.Secp256k1": {
"type": "Transitive",
"resolved": "3.1.4",
"contentHash": "23N1DCusSRCx1hoNiIMl3JnMZrdY78a/WcsiN1LIAg6sq8MiC7mszDiUgHKD6txm+m9PxJBigBLH7MPBQCRCDQ=="
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.1",
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==",
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.6",
"SQLitePCLRaw.provider.e_sqlite3": "2.1.6"
}
},
"SQLitePCLRaw.core": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"SQLitePCLRaw.provider.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
}
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ=="
},
"System.Interactive.Async": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "f8H1O4ZWDQo344y5NQU76G4SIjWMuKDVXL9OM1dg6K5YZnLkc8iCdQDybBvMcC6ufk61jzXGVAX6UCDu0qDSjA==",
"dependencies": {
"System.Linq.Async": "6.0.1"
}
},
"System.Linq.Async": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "6.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"System.Threading.Channels": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA=="
},
"walletwasabi": {
"type": "Project",
"dependencies": {
"Microsoft.AspNetCore.WebUtilities": "[8.0.0, )",
"Microsoft.Data.Sqlite": "[8.0.0, )",
"Microsoft.Extensions.Caching.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Http": "[8.0.0, )",
"Microsoft.Win32.SystemEvents": "[8.0.0, )",
"NBitcoin": "[7.0.27, )",
"NNostr.Client": "[0.0.49, )",
"System.IO.Pipelines": "[8.0.0, )",
"System.Text.Json": "[8.0.4, )",
"WabiSabi": "[1.0.1.2, )"
}
},
"Microsoft.AspNetCore.WebUtilities": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "z1SXKg5Bk02VmrrOab1TO2yxkZIfL4RyrS+yCpwxcLTqJwImYhEttz3LYbl1gQebkAAvx2Fm4NVXmopxXeLZgw==",
"dependencies": {
"Microsoft.Net.Http.Headers": "8.0.0",
"System.IO.Pipelines": "8.0.0"
}
},
"Microsoft.Data.Sqlite": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "H+iC5IvkCCKSNHXzL3JARvDn7VpkvuJM91KVB89sKjeTF/KX/BocNNh93ZJtX5MCQKb/z4yVKgkU2sVIq+xKfg==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "8.0.0",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
}
},
"Microsoft.Extensions.Caching.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0",
"Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Http": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Diagnostics": "8.0.0",
"Microsoft.Extensions.Logging": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw=="
},
"NBitcoin": {
"type": "CentralTransitive",
"requested": "[7.0.27, )",
"resolved": "7.0.27",
"contentHash": "n2eHYJf0YVOf3ld0fhQJ8qR8TDvGZObGseOf5gHx03QpG+lq5L5qJAn5SA+MvZQLKcqhEUJ+S2AKvWkgZYS4Gw==",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "1.0.0",
"Newtonsoft.Json": "13.0.1"
}
},
"NNostr.Client": {
"type": "CentralTransitive",
"requested": "[0.0.49, )",
"resolved": "0.0.49",
"contentHash": "UqmOBSCuUxw6qTk5+3TTWWtlyetx1btEuBJIS9beidviV45iMjlgOMK0ThB1CrYuPfg4DDzMu5rpzRYnM8eHWA==",
"dependencies": {
"LinqKit.Core": "1.2.5",
"NBitcoin.Secp256k1": "3.1.4",
"System.Interactive.Async": "6.0.1",
"System.Text.Json": "8.0.3",
"System.Threading.Channels": "8.0.0"
}
},
"System.IO.Pipelines": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA=="
},
"System.Text.Json": {
"type": "CentralTransitive",
"requested": "[8.0.4, )",
"resolved": "8.0.4",
"contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
"dependencies": {
"System.Text.Encodings.Web": "8.0.0"
}
},
"WabiSabi": {
"type": "CentralTransitive",
"requested": "[1.0.1.2, )",
"resolved": "1.0.1.2",
"contentHash": "e+pMZGVEfWQvkpZHAydGv6grY71urfO47lodjXC9eWtfSFvNtPWjrgqck9O24yIbXhP4K3QrJKzJQFGpAp8rqg==",
"dependencies": {
"NBitcoin.Secp256k1": "3.1.0"
}
}
},
"net8.0/linux-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"Microsoft.Win32.SystemEvents": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw=="
}
},
"net8.0/osx-arm64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"Microsoft.Win32.SystemEvents": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw=="
}
},
"net8.0/osx-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"Microsoft.Win32.SystemEvents": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw=="
}
},
"net8.0/win-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"Microsoft.Win32.SystemEvents": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw=="
}
}
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -1,25 +0,0 @@
# If you are not allowed to run this script, run the following command in your PowerShell console:
# Set-ExecutionPolicy RemoteSigned
$host.UI.RawUI.ForegroundColor = "Green"
$host.UI.RawUI.BackgroundColor = "Black"
Read-Host -Prompt 'Releasing Wasabi Wallet - Insert a pendrive to store macOS notarization candidate files [Press ENTER]'
Read-Host -Prompt 'Start Kleopatra!'
cd $env:userprofile\desktop/WalletWasabi/WalletWasabi.Packager
dotnet run -- publish
$host.UI.RawUI.ForegroundColor = "Green"
$host.UI.RawUI.BackgroundColor = "Black"
Read-Host -Prompt 'Remove and plug the pendrive to macOS and run the packager to notarize the files.'
$arguments = $env:userprofile + '\Desktop\WalletWasabi\WalletWasabi.WindowsInstaller\WalletWasabi.WindowsInstaller.wixproj /Build "Release|x64"'
Start-Process -FilePath 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.com' -ArgumentList $arguments # If -Wait -NoNewWindow added devenv will hang forever at the end of the build.
$host.UI.RawUI.ForegroundColor = "Green"
$host.UI.RawUI.BackgroundColor = "Black"
Read-Host -Prompt 'Wait until WiX building the MSI installer, then [Press ENTER]'
Read-Host -Prompt 'Wait until macOS notarization is done and insert the pendrive to this PC [Press ENTER]'
dotnet run -- sign
Read-Host -Prompt 'Release finished [Press ENTER]'

Просмотреть файл

@ -1,22 +0,0 @@
using WalletWasabi.Packager;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.Packager;
/// <summary>
/// Tests for <see cref="ArgsProcessor"/> class.
/// </summary>
public class ArgsProcessorTests
{
[Theory]
[InlineData(new string[] { "-onlybinaries" }, true)]
[InlineData(new string[] { "-onlyBinaries" }, true)]
[InlineData(new string[] { "-OnlyBinaries" }, true)]
[InlineData(new string[] { "---OnlyBinaries" }, true)]
[InlineData(new string[] { "---0nlyBinaries" }, false)]
public void IsOnlyBinariesModeTest(string[] input, bool expectedResult)
{
var argsProcessor = new ArgsProcessor(input);
Assert.Equal(expectedResult, argsProcessor.IsOnlyBinariesMode());
}
}

Просмотреть файл

@ -36,7 +36,6 @@
<ProjectReference Include="..\WalletWasabi.Backend\WalletWasabi.Backend.csproj" />
<ProjectReference Include="..\WalletWasabi.Fluent.Generators\WalletWasabi.Fluent.Generators.csproj" />
<ProjectReference Include="..\WalletWasabi.Fluent\WalletWasabi.Fluent.csproj" />
<ProjectReference Include="..\WalletWasabi.Packager\WalletWasabi.Packager.csproj" />
<ProjectReference Include="..\WalletWasabi\WalletWasabi.csproj" />
</ItemGroup>

Просмотреть файл

@ -27,8 +27,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
flake.nix = flake.nix
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WalletWasabi.Packager", "WalletWasabi.Packager\WalletWasabi.Packager.csproj", "{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "WalletWasabi.WindowsInstaller", "WalletWasabi.WindowsInstaller\WalletWasabi.WindowsInstaller.wixproj", "{54654468-4F58-4253-84BD-6F53A8D3D2C4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WalletWasabi.Fluent.Desktop", "WalletWasabi.Fluent.Desktop\WalletWasabi.Fluent.Desktop.csproj", "{177A8AC0-7879-4719-A8A9-49E1D75B3A2B}"
@ -71,14 +69,6 @@ Global
{526AC796-6CC0-4ADA-9A7E-C12267719A1A}.Release|Any CPU.Build.0 = Release|Any CPU
{526AC796-6CC0-4ADA-9A7E-C12267719A1A}.Release|x64.ActiveCfg = Debug|Any CPU
{526AC796-6CC0-4ADA-9A7E-C12267719A1A}.Release|x64.Build.0 = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Debug|x64.ActiveCfg = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Debug|x64.Build.0 = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Release|Any CPU.Build.0 = Release|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Release|x64.ActiveCfg = Debug|Any CPU
{8BE916A0-1F3F-4757-BAD9-BFBAB8EAC3C2}.Release|x64.Build.0 = Debug|Any CPU
{54654468-4F58-4253-84BD-6F53A8D3D2C4}.Debug|Any CPU.ActiveCfg = Debug|x64
{54654468-4F58-4253-84BD-6F53A8D3D2C4}.Debug|x64.ActiveCfg = Release|x64
{54654468-4F58-4253-84BD-6F53A8D3D2C4}.Release|Any CPU.ActiveCfg = Release|x64

Просмотреть файл

@ -80,7 +80,7 @@ public class TerminateService
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// This event will only be triggered if you run Wasabi from the published package. Use the packager with the --OnlyBinaries option.
// This event will only be triggered if you run Wasabi from the published package.
Logger.LogInfo($"Process termination was requested by the OS, reason '{e.Reason}'.");
e.Cancel = true;
}