Skip to content

Commit

Permalink
Merge pull request #847 from WildGums/GitHubSync/20241128-110203
Browse files Browse the repository at this point in the history
GitHubSync update
  • Loading branch information
GeertvanHorrik authored Nov 28, 2024
2 parents 5dab9e2 + 5b06eab commit 3dd76e0
Show file tree
Hide file tree
Showing 9 changed files with 463 additions and 151 deletions.
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

0 comments on commit 3dd76e0

Please sign in to comment.