Skip to content

Commit f887270

Browse files
Merge pull request #737 from WildGums/GitHubSync/20241128-110924
GitHubSync update
2 parents 1581b2e + ac098b3 commit f887270

9 files changed

+463
-151
lines changed

deployment/cake/apps-wpf-tasks.cake

+4-19
Original file line numberDiff line numberDiff line change
@@ -155,26 +155,11 @@ public class WpfProcessor : ProcessorBase
155155
CakeContext.DeleteFiles(filesToDelete);
156156
}
157157

158-
// We know we *highly likely* need to sign, so try doing this upfront
159-
if (!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName))
158+
if (BuildContext.General.CodeSign.IsAvailable ||
159+
BuildContext.General.AzureCodeSign.IsAvailable)
160160
{
161-
BuildContext.CakeContext.Information("Searching for packagable files to sign:");
162-
163-
var projectFilesToSign = new List<FilePath>();
164-
165-
var exeSignFilesSearchPattern = $"{BuildContext.General.OutputRootDirectory}/{wpfApp}/**/*.exe";
166-
BuildContext.CakeContext.Information($" - {exeSignFilesSearchPattern}");
167-
projectFilesToSign.AddRange(BuildContext.CakeContext.GetFiles(exeSignFilesSearchPattern));
168-
169-
var dllSignFilesSearchPattern = $"{BuildContext.General.OutputRootDirectory}/{wpfApp}/**/*.dll";
170-
BuildContext.CakeContext.Information($" - {dllSignFilesSearchPattern}");
171-
projectFilesToSign.AddRange(BuildContext.CakeContext.GetFiles(dllSignFilesSearchPattern));
172-
173-
var signToolCommand = string.Format("sign /a /t {0} /n {1} /fd {2}", BuildContext.General.CodeSign.TimeStampUri,
174-
BuildContext.General.CodeSign.CertificateSubjectName, BuildContext.General.CodeSign.HashAlgorithm);
175-
176-
SignFiles(BuildContext, signToolCommand, projectFilesToSign);
177-
}
161+
SignFilesInDirectory(BuildContext, outputDirectory, string.Empty);
162+
}
178163
else
179164
{
180165
BuildContext.CakeContext.Warning("No signing certificate subject name provided, not signing any files");

deployment/cake/components-tasks.cake

+19-21
Original file line numberDiff line numberDiff line change
@@ -314,27 +314,7 @@ public class ComponentsProcessor : ProcessorBase
314314
BuildContext.CakeContext.LogSeparator();
315315
}
316316

317-
var codeSign = (!BuildContext.General.IsCiBuild &&
318-
!BuildContext.General.IsLocalBuild &&
319-
!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName));
320-
if (codeSign)
321-
{
322-
// For details, see https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package
323-
// nuget sign MyPackage.nupkg -CertificateSubjectName <MyCertSubjectName> -Timestamper <TimestampServiceURL>
324-
var filesToSign = CakeContext.GetFiles($"{BuildContext.General.OutputRootDirectory}/*.nupkg");
325-
326-
foreach (var fileToSign in filesToSign)
327-
{
328-
CakeContext.Information($"Signing NuGet package '{fileToSign}' using certificate subject '{BuildContext.General.CodeSign.CertificateSubjectName}'");
329-
330-
var exitCode = CakeContext.StartProcess(BuildContext.General.NuGet.Executable, new ProcessSettings
331-
{
332-
Arguments = $"sign \"{fileToSign}\" -CertificateSubjectName \"{BuildContext.General.CodeSign.CertificateSubjectName}\" -Timestamper \"{BuildContext.General.CodeSign.TimeStampUri}\""
333-
});
334-
335-
CakeContext.Information("Signing NuGet package exited with '{0}'", exitCode);
336-
}
337-
}
317+
await SignNuGetPackageAsync();
338318
}
339319

340320
public override async Task DeployAsync()
@@ -378,4 +358,22 @@ public class ComponentsProcessor : ProcessorBase
378358
{
379359

380360
}
361+
362+
private async Task SignNuGetPackageAsync()
363+
{
364+
if (BuildContext.General.IsCiBuild ||
365+
BuildContext.General.IsLocalBuild)
366+
{
367+
return;
368+
}
369+
370+
// For details, see https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package
371+
// nuget sign MyPackage.nupkg -CertificateSubjectName <MyCertSubjectName> -Timestamper <TimestampServiceURL>
372+
var filesToSign = CakeContext.GetFiles($"{BuildContext.General.OutputRootDirectory}/*.nupkg");
373+
374+
foreach (var fileToSign in filesToSign)
375+
{
376+
SignNuGetPackage(BuildContext, fileToSign.FullPath);
377+
}
378+
}
381379
}

deployment/cake/generic-tasks.cake

+3-3
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,10 @@ Task("CodeSign")
276276
return;
277277
}
278278

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

deployment/cake/generic-variables.cake

+82-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class GeneralContext : BuildContextWithItemsBase
3939
public SolutionContext Solution { get; set; }
4040
public SourceLinkContext SourceLink { get; set; }
4141
public CodeSignContext CodeSign { get; set; }
42+
public AzureCodeSignContext AzureCodeSign { get; set; }
4243
public RepositoryContext Repository { get; set; }
4344
public SonarQubeContext SonarQube { get; set; }
4445

@@ -338,14 +339,27 @@ public class CodeSignContext : BuildContextBase
338339
public string TimeStampUri { get; set; }
339340
public string HashAlgorithm { get; set; }
340341

342+
public bool IsAvailable
343+
{
344+
get
345+
{
346+
if (string.IsNullOrWhiteSpace(CertificateSubjectName))
347+
{
348+
return false;
349+
}
350+
351+
return true;
352+
}
353+
}
354+
341355
protected override void ValidateContext()
342356
{
343357

344358
}
345359

346360
protected override void LogStateInfoForContext()
347361
{
348-
if (string.IsNullOrWhiteSpace(CertificateSubjectName))
362+
if (!IsAvailable)
349363
{
350364
CakeContext.Information($"Code signing is not configured");
351365
return;
@@ -359,6 +373,62 @@ public class CodeSignContext : BuildContextBase
359373

360374
//-------------------------------------------------------------
361375

376+
public class AzureCodeSignContext : BuildContextBase
377+
{
378+
public AzureCodeSignContext(IBuildContext parentBuildContext)
379+
: base(parentBuildContext)
380+
{
381+
}
382+
383+
public string VaultName { get; set; }
384+
public string VaultUrl { get { return $"https://{VaultName}.vault.azure.net"; } }
385+
public string CertificateName { get; set; }
386+
public string TimeStampUri { get; set; }
387+
public string HashAlgorithm { get; set; }
388+
public string TenantId { get; set; }
389+
public string ClientId { get; set; }
390+
public string ClientSecret { get; set; }
391+
392+
public bool IsAvailable
393+
{
394+
get
395+
{
396+
if (string.IsNullOrWhiteSpace(VaultName) ||
397+
string.IsNullOrWhiteSpace(CertificateName) ||
398+
string.IsNullOrWhiteSpace(TenantId) ||
399+
string.IsNullOrWhiteSpace(ClientId) ||
400+
string.IsNullOrWhiteSpace(ClientSecret))
401+
{
402+
return false;
403+
}
404+
405+
return true;
406+
}
407+
}
408+
409+
protected override void ValidateContext()
410+
{
411+
412+
}
413+
414+
protected override void LogStateInfoForContext()
415+
{
416+
if (!IsAvailable)
417+
{
418+
CakeContext.Information($"Azure Code signing is not configured");
419+
return;
420+
}
421+
422+
CakeContext.Information($"Azure Code vault name: '{VaultName}'");
423+
CakeContext.Information($"Azure Code vault URL: '{VaultUrl}'");
424+
CakeContext.Information($"Azure Code signing certificate name: '{CertificateName}'");
425+
CakeContext.Information($"Azure Code signing timestamp uri: '{TimeStampUri}'");
426+
CakeContext.Information($"Azure Code signing hash algorithm: '{HashAlgorithm}'");
427+
}
428+
}
429+
430+
//-------------------------------------------------------------
431+
362432
public class RepositoryContext : BuildContextBase
363433
{
364434
public RepositoryContext(IBuildContext parentBuildContext)
@@ -498,6 +568,17 @@ private GeneralContext InitializeGeneralContext(BuildContext buildContext, IBuil
498568
HashAlgorithm = buildContext.BuildServer.GetVariable("CodeSignHashAlgorithm", "SHA256", showValue: true)
499569
};
500570

571+
data.AzureCodeSign = new AzureCodeSignContext(data)
572+
{
573+
VaultName = buildContext.BuildServer.GetVariable("AzureCodeSignVaultName", showValue: true),
574+
CertificateName = buildContext.BuildServer.GetVariable("AzureCodeSignCertificateName", showValue: true),
575+
TimeStampUri = buildContext.BuildServer.GetVariable("AzureCodeSignTimeStampUri", "http://timestamp.digicert.com", showValue: true),
576+
HashAlgorithm = buildContext.BuildServer.GetVariable("AzureCodeSignHashAlgorithm", "SHA256", showValue: true),
577+
TenantId = buildContext.BuildServer.GetVariable("AzureCodeSignTenantId", showValue: false),
578+
ClientId = buildContext.BuildServer.GetVariable("AzureCodeSignClientId", showValue: false),
579+
ClientSecret = buildContext.BuildServer.GetVariable("AzureCodeSignClientSecret", showValue: false),
580+
};
581+
501582
data.Repository = new RepositoryContext(data)
502583
{
503584
Url = buildContext.BuildServer.GetVariable("RepositoryUrl", showValue: true),

deployment/cake/installers-innosetup.cake

+100-20
Original file line numberDiff line numberDiff line change
@@ -73,33 +73,64 @@ public class InnoSetupInstaller : IInstaller
7373
fileContents = fileContents.Replace("[VERSION_DISPLAY]", BuildContext.General.Version.FullSemVer);
7474
fileContents = fileContents.Replace("[WIZARDIMAGEFILE]", string.Format("logo_large{0}", setupSuffix));
7575

76-
var signTool = string.Empty;
77-
if (!string.IsNullOrWhiteSpace(BuildContext.General.CodeSign.CertificateSubjectName))
76+
var signToolIndex = GetRandomSignToolIndex();
77+
78+
try
7879
{
79-
signTool = string.Format("SignTool={0}", BuildContext.General.CodeSign.CertificateSubjectName);
80-
}
80+
var codeSignContext = BuildContext.General.CodeSign;
81+
var azureCodeSignContext = BuildContext.General.AzureCodeSign;
82+
83+
var signTool = string.Empty;
8184

82-
fileContents = fileContents.Replace("[SIGNTOOL]", signTool);
83-
System.IO.File.WriteAllText(innoSetupScriptFileName, fileContents);
85+
var signToolFileName = GetSignToolFileName(BuildContext);
86+
if (!string.IsNullOrWhiteSpace(signToolFileName))
87+
{
88+
var signToolName = DateTime.Now.ToString("yyyyMMddHHmmss");
89+
var signToolCommandLine = GetSignToolCommandLine(BuildContext);
8490

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

87-
BuildContext.CakeContext.InnoSetup(innoSetupScriptFileName, new InnoSetupSettings
88-
{
89-
OutputDirectory = innoSetupReleasesRoot
90-
});
93+
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(GetRegistryKey(), true))
94+
{
95+
var registryValueName = GetSignToolIndexName(signToolIndex);
9196

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

96-
// Copy the following files:
97-
// - Setup.exe => [projectName]-[version].exe
98-
// - Setup.exe => [projectName]-[channel].exe
100+
registryKey.SetValue(registryValueName, signToolRegistryValue);
101+
}
102+
103+
signTool = string.Format("SignTool={0}", signToolName);
104+
}
105+
106+
fileContents = fileContents.Replace("[SIGNTOOL]", signTool);
107+
System.IO.File.WriteAllText(innoSetupScriptFileName, fileContents);
108+
109+
BuildContext.CakeContext.Information("Generating Inno Setup packages, this can take a while, especially when signing is enabled...");
110+
111+
BuildContext.CakeContext.InnoSetup(innoSetupScriptFileName, new InnoSetupSettings
112+
{
113+
OutputDirectory = innoSetupReleasesRoot
114+
});
99115

100-
var installerSourceFile = System.IO.Path.Combine(innoSetupReleasesRoot, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe");
101-
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe"));
102-
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}{setupSuffix}.exe"));
116+
if (BuildContext.Wpf.UpdateDeploymentsShare)
117+
{
118+
BuildContext.CakeContext.Information("Copying Inno Setup files to deployments share at '{0}'", installersOnDeploymentsShare);
119+
120+
// Copy the following files:
121+
// - Setup.exe => [projectName]-[version].exe
122+
// - Setup.exe => [projectName]-[channel].exe
123+
124+
var installerSourceFile = System.IO.Path.Combine(innoSetupReleasesRoot, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe");
125+
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}_{BuildContext.General.Version.FullSemVer}.exe"));
126+
BuildContext.CakeContext.CopyFile(installerSourceFile, System.IO.Path.Combine(installersOnDeploymentsShare, $"{projectName}{setupSuffix}.exe"));
127+
}
128+
}
129+
finally
130+
{
131+
BuildContext.CakeContext.Information("Removing random sign tool config for Inno Setup");
132+
133+
RemoveSignToolFromRegistry(signToolIndex);
103134
}
104135
}
105136

@@ -222,4 +253,53 @@ public class InnoSetupInstaller : IInstaller
222253

223254
return installersOnDeploymentsShare;
224255
}
256+
257+
//-------------------------------------------------------------
258+
259+
private string GetRegistryKey()
260+
{
261+
return "Software\\Jordan Russell\\Inno Setup\\SignTools";
262+
}
263+
264+
//-------------------------------------------------------------
265+
266+
private int GetRandomSignToolIndex()
267+
{
268+
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(GetRegistryKey()))
269+
{
270+
for (int i = 0; i < 100; i++)
271+
{
272+
var valueName = GetSignToolIndexName(i);
273+
274+
if (registryKey.GetValue(valueName) is null)
275+
{
276+
// Immediately lock it
277+
registryKey.SetValue(valueName, "reserved");
278+
279+
return i;
280+
}
281+
}
282+
}
283+
284+
throw new Exception("Could not find any empty slots for the sign tool, please clean up the sign tool registry for Inno Setup");
285+
}
286+
287+
//-------------------------------------------------------------
288+
289+
private string GetSignToolIndexName(int index)
290+
{
291+
return $"SignTool{index}";
292+
}
293+
294+
//-------------------------------------------------------------
295+
296+
private void RemoveSignToolFromRegistry(int index)
297+
{
298+
using (var registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(GetRegistryKey()))
299+
{
300+
var valueName = GetSignToolIndexName(index);
301+
302+
registryKey.DeleteValue(valueName, false);
303+
}
304+
}
225305
}

0 commit comments

Comments
 (0)