using System.Security.Cryptography; namespace ReleaseBuilder.Build; public static partial class Command { /// /// Setup of the current runtime information /// private class RuntimeConfig { /// /// Constructs a new /// /// The release info to use /// The keyfile password to use /// The sign keys /// The changelog news /// The command input public RuntimeConfig(ReleaseInfo releaseInfo, IEnumerable signKeys, string keyfilePassword, string changelogNews, CommandInput input) { ReleaseInfo = releaseInfo; SignKeys = signKeys; KeyfilePassword = keyfilePassword; ChangelogNews = changelogNews; Input = input; } /// /// The cached password for the pfx file /// private string? _pfxPassword = null; /// /// The commandline input /// private CommandInput Input { get; } /// /// The release info for this run /// public ReleaseInfo ReleaseInfo { get; } /// /// The keyfile password for this run /// public IEnumerable SignKeys { get; } /// /// The primary password /// public string KeyfilePassword { get; } /// /// The changelog news /// public string ChangelogNews { get; } /// /// Gets the PFX password and throws if not possible /// public string PfxPassword => string.IsNullOrWhiteSpace(_pfxPassword) ? _pfxPassword = GetAuthenticodePassword(KeyfilePassword) : _pfxPassword; /// /// Cache value for checking if authenticode signing is enabled /// private bool? _useAuthenticodeSigning; /// /// Checks if Authenticode signing should be enabled /// public void ToggleAuthenticodeSigning() { if (!_useAuthenticodeSigning.HasValue) { if (Input.DisableAuthenticode) { _useAuthenticodeSigning = false; return; } if (Program.Configuration.IsAuthenticodePossible()) _useAuthenticodeSigning = true; else { if (ConsoleHelper.ReadInput("Configuration missing for osslsigncode, continue without signing executables?", "Y", "n") == "Y") { _useAuthenticodeSigning = false; return; } throw new Exception("Configuration is not set up for osslsigncode"); } } } /// /// Cache value for checking if codesign is possible /// private bool? _useCodeSignSigning; /// /// Checks if codesign is enabled /// public void ToggleSignCodeSigning() { if (!_useCodeSignSigning.HasValue) { if (Input.DisableSignCode) { _useCodeSignSigning = false; return; } if (!OperatingSystem.IsMacOS()) _useCodeSignSigning = false; else if (Program.Configuration.IsCodeSignPossible()) _useCodeSignSigning = true; else { if (ConsoleHelper.ReadInput("Configuration missing for signcode, continue without signing executables?", "Y", "n") == "Y") { _useCodeSignSigning = false; return; } throw new Exception("Configuration is not set up for signcode"); } } } /// /// Cache value for checking if docker build is enabled /// private bool? _dockerBuild; /// /// Checks if docker build is enabled /// public async Task ToggleDockerBuild() { if (!_dockerBuild.HasValue) { try { var res = await ProcessHelper.ExecuteWithOutput([Program.Configuration.Commands.Docker!, "ps"], suppressStdErr: true); _dockerBuild = true; } catch { if (ConsoleHelper.ReadInput("Docker does not seem to be running, continue without docker builds?", "Y", "n") == "Y") { _dockerBuild = false; return; } throw new Exception("Docker is not running, and is required for building Docker images"); } } } /// /// Cache value for checking if notarize is enabled /// private bool? _useNotarizeSigning; /// /// Checks if notarize signing is enabled /// public void ToggleNotarizeSigning() { if (!_useNotarizeSigning.HasValue) { if (Input.DisableNotarizeSigning) { _useNotarizeSigning = false; return; } if (!OperatingSystem.IsMacOS()) _useNotarizeSigning = false; else if (Program.Configuration.IsNotarizePossible()) _useNotarizeSigning = true; else { if (ConsoleHelper.ReadInput("Configuration missing for notarize, continue without notarizing executables?", "Y", "n") == "Y") { _useNotarizeSigning = false; return; } throw new Exception("Configuration is not set up for notarize"); } } } /// /// Cache value for checking if GPG signing is enabled /// private bool? _useGpgSigning; /// /// Checks if GPG signing is enabled /// public void ToggleGpgSigning() { if (!_useGpgSigning.HasValue) { if (Input.DisableGpgSigning) { _useGpgSigning = false; return; } if (Program.Configuration.IsGpgPossible()) _useGpgSigning = true; else { if (ConsoleHelper.ReadInput("Configuration missing for gpg, continue without gpg signing packages?", "Y", "n") == "Y") { _useGpgSigning = false; return; } throw new Exception("Configuration is not set up for gpg"); } } } /// /// Cache value for checking if S3 upload is enabled /// private bool? _useS3Upload; /// /// Checks if S3 upload is enabled /// public void ToggleS3Upload() { if (!_useS3Upload.HasValue) { if (Input.DisableS3Upload) { _useS3Upload = false; return; } if (Program.Configuration.IsAwsUploadPossible()) _useS3Upload = true; else { if (ConsoleHelper.ReadInput("Configuration missing for awscli, continue without uploading to S3?", "Y", "n") == "Y") { _useS3Upload = false; return; } throw new Exception("Configuration is not set up for awscli"); } } } /// /// Cache value for checking if Github upload is enabled /// private bool? _useGithubUpload; /// /// Checks if Github upload is enabled /// /// The release channel to use public void ToggleGithubUpload(ReleaseChannel channel) { if (!_useGithubUpload.HasValue) { if (Input.DisableGithubUpload || channel == ReleaseChannel.Debug || channel == ReleaseChannel.Nightly) { _useGithubUpload = false; return; } if (Program.Configuration.IsGithubUploadPossible()) _useGithubUpload = true; else { if (ConsoleHelper.ReadInput("Configuration is missing a Github token, continue without uploading to Github?", "Y", "n") == "Y") { _useGithubUpload = false; return; } throw new Exception("Configuration is not set up for github releases"); } } } /// /// Cache value for checking if update server reload is enabled /// private bool? _useUpdateServerReload; /// /// Checks if update server reload is enabled /// public void ToggleUpdateServerReload() { if (!_useUpdateServerReload.HasValue) { if (Input.DisableUpdateServerReload) { _useUpdateServerReload = false; return; } if (Program.Configuration.IsUpdateServerReloadPossible()) _useUpdateServerReload = true; else { if (ConsoleHelper.ReadInput("Configuration missing for update server, continue without reloading the update server?", "Y", "n") == "Y") { _useUpdateServerReload = false; return; } throw new Exception("Configuration is not set up for update server"); } } } /// /// Cache value for checking if forum posting is enabled /// private bool? _useDiscourseAnnounce; /// /// Checks if forum posting is enabled /// /// The release channel to use public void ToogleDiscourseAnnounce(ReleaseChannel channel) { if (!_useDiscourseAnnounce.HasValue) { if (Input.DisableDiscordAnnounce || channel == ReleaseChannel.Debug || channel == ReleaseChannel.Nightly) { _useDiscourseAnnounce = false; return; } if (Program.Configuration.IsDiscourseAnnouncePossible()) _useDiscourseAnnounce = true; else { if (ConsoleHelper.ReadInput("Configuration missing for forum posting, continue without posting to the forum?", "Y", "n") == "Y") { _useDiscourseAnnounce = false; return; } throw new Exception("Configuration is not set up for forum posting"); } } } /// /// Returns a value indicating if codesign is enabled /// public bool UseCodeSignSigning => _useCodeSignSigning!.Value; /// /// Returns a value indicating if authenticode signing is enabled /// public bool UseAuthenticodeSigning => _useAuthenticodeSigning!.Value; /// /// Returns a value indicating if notarize is enabled /// public bool UseNotarizeSigning => _useNotarizeSigning!.Value; /// /// Returns a value indicating if GPG signing is enabled /// public bool UseGPGSigning => _useGpgSigning!.Value; /// /// Returns a value indicating if docker build is enabled /// public bool UseDockerBuild => _dockerBuild!.Value; /// /// Returns a value indicating if S3 upload is enabled /// public bool UseS3Upload => _useS3Upload!.Value; /// /// Returns a value indicating if Github upload is enabled /// public bool UseGithubUpload => _useGithubUpload!.Value; /// /// Returns a value indicating if update server reload is enabled /// public bool UseUpdateServerReload => _useUpdateServerReload!.Value; /// /// Returns a value indicating if forum posting is enabled /// public bool UseForumPosting => _useDiscourseAnnounce!.Value; /// /// Gets the MacOS app bundle name /// public string MacOSAppName => Input.MacOSAppName; /// /// The docker repository to use /// public string DockerRepo => Input.DockerRepo; /// /// Gets a value indicating if pushing should be enabled /// public bool PushToDocker => !Input.DisableDockerPush; /// /// Decrypts the password file and returns the PFX password /// /// Password for the password file /// The Authenticode password private string GetAuthenticodePassword(string keyfilepassword) => EncryptionHelper.DecryptPasswordFile(Program.Configuration.ConfigFiles.AuthenticodePasswordFile, keyfilepassword).Trim(); /// /// Performs authenticode signing if enabled /// /// The file to sign /// An awaitable task public Task AuthenticodeSign(string file) => UseAuthenticodeSigning ? ProcessRunner.OsslCodeSign( Program.Configuration.Commands.OsslSignCode!, Program.Configuration.ConfigFiles.AuthenticodePfxFile, PfxPassword, file) : Task.CompletedTask; /// /// Performs codesign on the given file /// /// The file to sign /// The entitlements to apply /// An awaitable task public Task Codesign(string file, string entitlements) => UseCodeSignSigning ? ProcessRunner.MacOSCodeSign( Program.Configuration.Commands.Codesign!, Program.Configuration.ConfigFiles.CodesignIdentity, entitlements, file ) : Task.CompletedTask; /// /// Performs productsign on the given file /// /// The file to sign /// An awaitable task public Task Productsign(string file) => UseCodeSignSigning ? ProcessRunner.MacOSProductSign( Program.Configuration.Commands.Productsign!, Program.Configuration.ConfigFiles.CodesignIdentity, file ) : Task.CompletedTask; } }