Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitHubSync update #637

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions deployment/cake/apps-wpf-tasks.cake
Original file line number Diff line number Diff line change
Expand Up @@ -155,26 +155,11 @@ public class WpfProcessor : ProcessorBase
CakeContext.DeleteFiles(filesToDelete);
}

// We know we *highly likely* need to sign, so try doing this upfront
if (!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName))
if (BuildContext.General.CodeSign.IsAvailable ||
BuildContext.General.AzureCodeSign.IsAvailable)
{
BuildContext.CakeContext.Information("Searching for packagable files to sign:");

var projectFilesToSign = new List<FilePath>();

var exeSignFilesSearchPattern = $"{BuildContext.General.OutputRootDirectory}/{wpfApp}/**/*.exe";
BuildContext.CakeContext.Information($" - {exeSignFilesSearchPattern}");
projectFilesToSign.AddRange(BuildContext.CakeContext.GetFiles(exeSignFilesSearchPattern));

var dllSignFilesSearchPattern = $"{BuildContext.General.OutputRootDirectory}/{wpfApp}/**/*.dll";
BuildContext.CakeContext.Information($" - {dllSignFilesSearchPattern}");
projectFilesToSign.AddRange(BuildContext.CakeContext.GetFiles(dllSignFilesSearchPattern));

var signToolCommand = string.Format("sign /a /t {0} /n {1} /fd {2}", BuildContext.General.CodeSign.TimeStampUri,
BuildContext.General.CodeSign.CertificateSubjectName, BuildContext.General.CodeSign.HashAlgorithm);

SignFiles(BuildContext, signToolCommand, projectFilesToSign);
}
SignFilesInDirectory(BuildContext, outputDirectory, string.Empty);
}
else
{
BuildContext.CakeContext.Warning("No signing certificate subject name provided, not signing any files");
Expand Down
40 changes: 19 additions & 21 deletions deployment/cake/components-tasks.cake
Original file line number Diff line number Diff line change
Expand Up @@ -314,27 +314,7 @@ public class ComponentsProcessor : ProcessorBase
BuildContext.CakeContext.LogSeparator();
}

var codeSign = (!BuildContext.General.IsCiBuild &&
!BuildContext.General.IsLocalBuild &&
!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName));
if (codeSign)
{
// For details, see https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package
// nuget sign MyPackage.nupkg -CertificateSubjectName <MyCertSubjectName> -Timestamper <TimestampServiceURL>
var filesToSign = CakeContext.GetFiles($"{BuildContext.General.OutputRootDirectory}/*.nupkg");

foreach (var fileToSign in filesToSign)
{
CakeContext.Information($"Signing NuGet package '{fileToSign}' using certificate subject '{BuildContext.General.CodeSign.CertificateSubjectName}'");

var exitCode = CakeContext.StartProcess(BuildContext.General.NuGet.Executable, new ProcessSettings
{
Arguments = $"sign \"{fileToSign}\" -CertificateSubjectName \"{BuildContext.General.CodeSign.CertificateSubjectName}\" -Timestamper \"{BuildContext.General.CodeSign.TimeStampUri}\""
});

CakeContext.Information("Signing NuGet package exited with '{0}'", exitCode);
}
}
await SignNuGetPackageAsync();
}

public override async Task DeployAsync()
Expand Down Expand Up @@ -378,4 +358,22 @@ public class ComponentsProcessor : ProcessorBase
{

}

private async Task SignNuGetPackageAsync()
{
if (BuildContext.General.IsCiBuild ||
BuildContext.General.IsLocalBuild)
{
return;
}

// For details, see https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package
// nuget sign MyPackage.nupkg -CertificateSubjectName <MyCertSubjectName> -Timestamper <TimestampServiceURL>
var filesToSign = CakeContext.GetFiles($"{BuildContext.General.OutputRootDirectory}/*.nupkg");

foreach (var fileToSign in filesToSign)
{
SignNuGetPackage(BuildContext, fileToSign.FullPath);
}
}
}
6 changes: 3 additions & 3 deletions deployment/cake/generic-tasks.cake
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,10 @@ Task("CodeSign")
return;
}

var certificateSubjectName = buildContext.General.CodeSign.CertificateSubjectName;
if (string.IsNullOrWhiteSpace(certificateSubjectName))
if (!buildContext.General.CodeSign.IsAvailable &&
!buildContext.General.AzureCodeSign.IsAvailable)
{
Information("Skipping code signing because the certificate subject name was not specified");
Information("Skipping code signing since no option is available");
return;
}

Expand Down
83 changes: 82 additions & 1 deletion deployment/cake/generic-variables.cake
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class GeneralContext : BuildContextWithItemsBase
public SolutionContext Solution { get; set; }
public SourceLinkContext SourceLink { get; set; }
public CodeSignContext CodeSign { get; set; }
public AzureCodeSignContext AzureCodeSign { get; set; }
public RepositoryContext Repository { get; set; }
public SonarQubeContext SonarQube { get; set; }

Expand Down Expand Up @@ -338,14 +339,27 @@ public class CodeSignContext : BuildContextBase
public string TimeStampUri { get; set; }
public string HashAlgorithm { get; set; }

public bool IsAvailable
{
get
{
if (string.IsNullOrWhiteSpace(CertificateSubjectName))
{
return false;
}

return true;
}
}

protected override void ValidateContext()
{

}

protected override void LogStateInfoForContext()
{
if (string.IsNullOrWhiteSpace(CertificateSubjectName))
if (!IsAvailable)
{
CakeContext.Information($"Code signing is not configured");
return;
Expand All @@ -359,6 +373,62 @@ public class CodeSignContext : BuildContextBase

//-------------------------------------------------------------

public class AzureCodeSignContext : BuildContextBase
{
public AzureCodeSignContext(IBuildContext parentBuildContext)
: base(parentBuildContext)
{
}

public string VaultName { get; set; }
public string VaultUrl { get { return $"https://{VaultName}.vault.azure.net"; } }
public string CertificateName { get; set; }
public string TimeStampUri { get; set; }
public string HashAlgorithm { get; set; }
public string TenantId { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }

public bool IsAvailable
{
get
{
if (string.IsNullOrWhiteSpace(VaultName) ||
string.IsNullOrWhiteSpace(CertificateName) ||
string.IsNullOrWhiteSpace(TenantId) ||
string.IsNullOrWhiteSpace(ClientId) ||
string.IsNullOrWhiteSpace(ClientSecret))
{
return false;
}

return true;
}
}

protected override void ValidateContext()
{

}

protected override void LogStateInfoForContext()
{
if (!IsAvailable)
{
CakeContext.Information($"Azure Code signing is not configured");
return;
}

CakeContext.Information($"Azure Code vault name: '{VaultName}'");
CakeContext.Information($"Azure Code vault URL: '{VaultUrl}'");
CakeContext.Information($"Azure Code signing certificate name: '{CertificateName}'");
CakeContext.Information($"Azure Code signing timestamp uri: '{TimeStampUri}'");
CakeContext.Information($"Azure Code signing hash algorithm: '{HashAlgorithm}'");
}
}

//-------------------------------------------------------------

public class RepositoryContext : BuildContextBase
{
public RepositoryContext(IBuildContext parentBuildContext)
Expand Down Expand Up @@ -498,6 +568,17 @@ private GeneralContext InitializeGeneralContext(BuildContext buildContext, IBuil
HashAlgorithm = buildContext.BuildServer.GetVariable("CodeSignHashAlgorithm", "SHA256", showValue: true)
};

data.AzureCodeSign = new AzureCodeSignContext(data)
{
VaultName = buildContext.BuildServer.GetVariable("AzureCodeSignVaultName", showValue: true),
CertificateName = buildContext.BuildServer.GetVariable("AzureCodeSignCertificateName", showValue: true),
TimeStampUri = buildContext.BuildServer.GetVariable("AzureCodeSignTimeStampUri", "http://timestamp.digicert.com", showValue: true),
HashAlgorithm = buildContext.BuildServer.GetVariable("AzureCodeSignHashAlgorithm", "SHA256", showValue: true),
TenantId = buildContext.BuildServer.GetVariable("AzureCodeSignTenantId", showValue: false),
ClientId = buildContext.BuildServer.GetVariable("AzureCodeSignClientId", showValue: false),
ClientSecret = buildContext.BuildServer.GetVariable("AzureCodeSignClientSecret", showValue: false),
};

data.Repository = new RepositoryContext(data)
{
Url = buildContext.BuildServer.GetVariable("RepositoryUrl", showValue: true),
Expand Down
120 changes: 100 additions & 20 deletions deployment/cake/installers-innosetup.cake
Original file line number Diff line number Diff line change
Expand Up @@ -73,33 +73,64 @@ public class InnoSetupInstaller : IInstaller
fileContents = fileContents.Replace("[VERSION_DISPLAY]", BuildContext.General.Version.FullSemVer);
fileContents = fileContents.Replace("[WIZARDIMAGEFILE]", string.Format("logo_large{0}", setupSuffix));

var signTool = string.Empty;
if (!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName))
var signToolIndex = GetRandomSignToolIndex();

try
{
signTool = string.Format("SignTool={0}", BuildContext.General.CodeSign.CertificateSubjectName);
}
var codeSignContext = BuildContext.General.CodeSign;
var azureCodeSignContext = BuildContext.General.AzureCodeSign;

var signTool = string.Empty;

fileContents = fileContents.Replace("[SIGNTOOL]", signTool);
System.IO.File.WriteAllText(innoSetupScriptFileName, fileContents);
var signToolFileName = GetSignToolFileName(BuildContext);
if (!string.IsNullOrWhiteSpace(signToolFileName))
{
var signToolName = DateTime.Now.ToString("yyyyMMddHHmmss");
var signToolCommandLine = GetSignToolCommandLine(BuildContext);

BuildContext.CakeContext.Information("Generating Inno Setup packages, this can take a while, especially when signing is enabled...");
BuildContext.CakeContext.Information("Adding random sign tool config for Inno Setup");

BuildContext.CakeContext.InnoSetup(innoSetupScriptFileName, new InnoSetupSettings
{
OutputDirectory = innoSetupReleasesRoot
});
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(GetRegistryKey(), true))
{
var registryValueName = GetSignToolIndexName(signToolIndex);

if (BuildContext.Wpf.UpdateDeploymentsShare)
{
BuildContext.CakeContext.Information("Copying Inno Setup files to deployments share at '{0}'", installersOnDeploymentsShare);
// Important: must end with "$f"
var signToolRegistryValue = $"{signToolName}=\"{signToolFileName}\" {signToolCommandLine} \"$f\"";

// Copy the following files:
// - Setup.exe => [projectName]-[version].exe
// - Setup.exe => [projectName]-[channel].exe
registryKey.SetValue(registryValueName, signToolRegistryValue);
}

signTool = string.Format("SignTool={0}", signToolName);
}

fileContents = fileContents.Replace("[SIGNTOOL]", signTool);
System.IO.File.WriteAllText(innoSetupScriptFileName, fileContents);

BuildContext.CakeContext.Information("Generating Inno Setup packages, this can take a while, especially when signing is enabled...");

BuildContext.CakeContext.InnoSetup(innoSetupScriptFileName, new InnoSetupSettings
{
OutputDirectory = innoSetupReleasesRoot
});

var installerSourceFile = System.IO.Path.Combine(innoSetupReleasesRoot, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe");
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe"));
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}{setupSuffix}.exe"));
if (BuildContext.Wpf.UpdateDeploymentsShare)
{
BuildContext.CakeContext.Information("Copying Inno Setup files to deployments share at '{0}'", installersOnDeploymentsShare);

// Copy the following files:
// - Setup.exe => [projectName]-[version].exe
// - Setup.exe => [projectName]-[channel].exe

var installerSourceFile = System.IO.Path.Combine(innoSetupReleasesRoot, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe");
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe"));
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}{setupSuffix}.exe"));
}
}
finally
{
BuildContext.CakeContext.Information("Removing random sign tool config for Inno Setup");

RemoveSignToolFromRegistry(signToolIndex);
}
}

Expand Down Expand Up @@ -222,4 +253,53 @@ public class InnoSetupInstaller : IInstaller

return installersOnDeploymentsShare;
}

//-------------------------------------------------------------

private string GetRegistryKey()
{
return "Software\\Jordan Russell\\Inno Setup\\SignTools";
}

//-------------------------------------------------------------

private int GetRandomSignToolIndex()
{
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(GetRegistryKey()))
{
for (int i = 0; i < 100; i++)
{
var valueName = GetSignToolIndexName(i);

if (registryKey.GetValue(valueName) is null)
{
// Immediately lock it
registryKey.SetValue(valueName, "reserved");

return i;
}
}
}

throw new Exception("Could not find any empty slots for the sign tool, please clean up the sign tool registry for Inno Setup");
}

//-------------------------------------------------------------

private string GetSignToolIndexName(int index)
{
return $"SignTool{index}";
}

//-------------------------------------------------------------

private void RemoveSignToolFromRegistry(int index)
{
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(GetRegistryKey()))
{
var valueName = GetSignToolIndexName(index);

registryKey.DeleteValue(valueName, false);
}
}
}
Loading