File: GenerateTestDevCert.cs
Web Access
Project: src\eng\tools\RepoTasks\RepoTasks.csproj (RepoTasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Linq;
using System.Text.Json;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
 
namespace RepoTasks;
 
/// <summary>
/// Generates the test HTTPs certificate used by the template tests
/// </summary>
public class GenerateTestDevCert : Task
{
    [Required]
    public string CertificatePath { get; private set; }
 
    public override bool Execute()
    {
        Mutex mutex = null;
        try
        {
            // MSBuild will potentially invoke this task in parallel across different subprocesses/nodes.
            // The build is configured to generate the certificate in a single location, but multiple projects
            // import the same targets that will use this task, which will result in multiple calls.
            // To avoid issues where we try to generate multiple certificates on the same location, we wrap the
            // usage in a named mutex which guarantees that only one instance will run at a time.
            mutex = new(initiallyOwned: true, "Global\\GenerateTestDevCert", out var createdNew);
            if (!createdNew)
            {
                // The mutex already exists, wait for it to be released.
                mutex.WaitOne();
            }
 
            if (File.Exists(CertificatePath))
            {
                Log.LogMessage(MessageImportance.Normal, $"A test certificate already exists at {CertificatePath}");
                return true;
            }
 
            var cert = DevelopmentCertificate.Create(CertificatePath);
 
            var devCertJsonFile = Path.ChangeExtension(CertificatePath, ".json");
            var devCertJson = new CertificateInfo
            {
                Password = cert.CertificatePassword,
                Thumbprint = cert.CertificateThumbprint
            };
 
            using var file = File.OpenWrite(devCertJsonFile);
            file.SetLength(0);
            JsonSerializer.Serialize(file, devCertJson);
        }
        catch (Exception e)
        {
            Log.LogErrorFromException(e, showStackTrace: true);
        }
        finally
        {
            mutex.ReleaseMutex();
        }
 
        return !Log.HasLoggedErrors;
    }
 
    private class CertificateInfo
    {
        public string Password { get; set; }
        public string Thumbprint { get; set; }
    }
}