build/BuildSteps.cs (304 lines of code) (raw):
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Build
{
public static class BuildSteps
{
public static void Clean()
{
if (FileUtility.DirectoryExists(Settings.RootBinDirectory))
{
Directory.Delete(Settings.RootBinDirectory, recursive: true);
}
if (FileUtility.DirectoryExists(Settings.RootBuildDirectory))
{
Directory.Delete(Settings.RootBuildDirectory, recursive: true);
}
if (FileUtility.DirectoryExists(Settings.ArtifactsDirectory))
{
Directory.Delete(Settings.ArtifactsDirectory, recursive: true);
}
if (FileUtility.DirectoryExists(Settings.ToolsDirectory))
{
Directory.Delete(Settings.ToolsDirectory, recursive: true);
}
}
public static void DownloadTemplates()
{
bool isLocalBuild = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BUILD_BUILDID"));
if (isLocalBuild)
{
return;
}
var files = Directory.GetFiles(Settings.TemplatesArtifactsDirectory);
string previewStr = BundleConfiguration.Instance.IsPreviewBundle ? ".Preview" : String.Empty;
string zipFileName = $"ExtensionBundle{previewStr}.v{BundleConfiguration.Instance.ExtensionBundleVersion[0]}.Templates";
foreach (string file in files)
{
var fileName = Path.GetFileName(file);
if (fileName.StartsWith(zipFileName))
{
zipFileName = fileName;
break;
}
}
Console.WriteLine($"Found matching templates in ${zipFileName}");
string zipFilePath = Path.Combine(Settings.TemplatesArtifactsDirectory, zipFileName);
FileUtility.EnsureDirectoryExists(Settings.TemplatesV1RootDirectory);
ZipFile.ExtractToDirectory(zipFilePath, Settings.TemplatesV1RootDirectory);
if (!FileUtility.DirectoryExists(Settings.TemplatesV1RootDirectory) || !FileUtility.FileExists(Settings.TemplatesJsonFilePath))
{
throw new Exception("Template download failed");
}
if (FileUtility.DirectoryExists(Settings.TemplatesV1RootDirectory) || FileUtility.FileExists(Settings.ResourcesFilePath))
{
FileUtility.CopyFile(Settings.ResourcesFilePath, Settings.ResourcesEnUSFilePath);
}
if (!FileUtility.DirectoryExists(Settings.TemplatesV1RootDirectory) || !FileUtility.FileExists(Settings.ResourcesEnUSFilePath))
{
throw new Exception("Resource Copy failed");
}
if (!FileUtility.DirectoryExists(Settings.TemplatesV2RootDirectory))
{
FileUtility.EnsureDirectoryExists(Settings.TemplatesV2RootDirectory);
}
if (FileUtility.DirectoryExists(Settings.TemplatesV2Directory))
{
Directory.Move(Settings.TemplatesV2Directory, Path.Join(Settings.TemplatesV2RootDirectory, "templates"));
}
if (FileUtility.DirectoryExists(Settings.ResourcesV2Directory))
{
Directory.Move(Settings.ResourcesV2Directory, Path.Join(Settings.TemplatesV2RootDirectory, "resources"));
}
if (FileUtility.DirectoryExists(Settings.BindingsV2Directory))
{
Directory.Move(Settings.BindingsV2Directory, Path.Join(Settings.TemplatesV2RootDirectory, "bindings"));
}
}
public static void BuildBundleBinariesForWindows()
{
Settings.WindowsBuildConfigurations.ForEach((config) => BuildExtensionsBundle(config).GetAwaiter().GetResult());
}
public static void BuildBundleBinariesForLinux()
{
Settings.LinuxBuildConfigurations.ForEach((config) => BuildExtensionsBundle(config).GetAwaiter().GetResult());
}
public static async Task<string> GenerateBundleProjectFile(BuildConfiguration buildConfig)
{
var sourceNugetConfig = Path.Combine(Settings.SourcePath, Settings.NugetConfigFileName);
var sourceProjectFilePath = Path.Combine(Settings.SourcePath, buildConfig.SourceProjectFileName);
string projectDirectory = Path.Combine(Settings.RootBuildDirectory, buildConfig.ConfigId.ToString());
string targetProjectFilePath = Path.Combine(Settings.RootBuildDirectory, projectDirectory, "extensions.csproj");
string targetNugetConfigFilePath = Path.Combine(Settings.RootBuildDirectory, projectDirectory, Settings.NugetConfigFileName);
FileUtility.EnsureDirectoryExists(projectDirectory);
FileUtility.CopyFile(sourceProjectFilePath, targetProjectFilePath);
FileUtility.CopyFile(sourceNugetConfig, targetNugetConfigFilePath);
await AddExtensionPackages(targetProjectFilePath, BundleConfiguration.Instance.IsPreviewBundle);
return targetProjectFilePath;
}
public static async Task AddExtensionPackages(string projectFilePath, bool addPrereleasePackages)
{
var extensions = GetExtensionList();
foreach (var extension in extensions)
{
string version = string.IsNullOrEmpty(extension.Version) ? await Helper.GetLatestPackageVersion(extension.Id, extension.MajorVersion, addPrereleasePackages) : extension.Version;
Shell.Run("dotnet", $"add {projectFilePath} package {extension.Id} -v {version} -n");
}
}
public static async Task BuildExtensionsBundle(BuildConfiguration buildConfig)
{
var projectFilePath = await GenerateBundleProjectFile(buildConfig);
var publishCommandArguments = $"publish {projectFilePath} -c Release -o {buildConfig.PublishDirectoryPath}";
if (!buildConfig.RuntimeIdentifier.Equals("any", StringComparison.OrdinalIgnoreCase))
{
publishCommandArguments += $" -r {buildConfig.RuntimeIdentifier}";
}
if (buildConfig.PublishReadyToRun)
{
publishCommandArguments += $" /p:PublishReadyToRun=true";
}
Shell.Run("dotnet", publishCommandArguments);
if (Path.Combine(buildConfig.PublishDirectoryPath, "bin") != buildConfig.PublishBinDirectoryPath)
{
FileUtility.EnsureDirectoryExists(Directory.GetParent(buildConfig.PublishBinDirectoryPath).FullName);
Directory.Move(Path.Combine(buildConfig.PublishDirectoryPath, "bin"), buildConfig.PublishBinDirectoryPath);
}
}
public static void GenerateVulnerabilityReport()
{
Settings.WindowsBuildConfigurations.ForEach((config) => RunVulnerabilityReport(config));
}
public static void RunVulnerabilityReport(BuildConfiguration buildConfig)
{
string projectDirectory = Path.Combine(Settings.RootBuildDirectory, buildConfig.ConfigId.ToString());
string projectFilePath = Path.Combine(Settings.RootBuildDirectory, projectDirectory, "extensions.csproj");
var currectDirectory = Directory.GetCurrentDirectory();
try
{
Directory.SetCurrentDirectory(Settings.RootBuildDirectory);
Console.WriteLine(Directory.GetCurrentDirectory());
Console.WriteLine($"dotnet list \"{projectFilePath}\" package --include-transitive --vulnerable");
string output = Shell.GetOutput("dotnet", $"list \"{projectFilePath}\" package --include-transitive --vulnerable");
if (!output.Contains("has no vulnerable packages given the current sources."))
{
Console.WriteLine(output);
throw new Exception($"Vulnerabilities found in {projectFilePath}");
}
}
finally
{
Directory.SetCurrentDirectory(currectDirectory);
}
}
public static void AddBindingInfoToExtensionsJson(string extensionsJson)
{
var extensionsJsonFileContent = FileUtility.ReadAllText(extensionsJson);
var outputExtensions = JsonConvert.DeserializeObject<BundleExtensions>(extensionsJsonFileContent);
var inputExtensions = GetExtensionList();
foreach (var extensionJsonEntry in outputExtensions.Extensions)
{
extensionJsonEntry.Bindings = inputExtensions.Where(
e =>
{
return extensionJsonEntry.Name.Equals(e.Name, StringComparison.OrdinalIgnoreCase);
}).First().Bindings;
}
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
FileUtility.Write(extensionsJson, JsonConvert.SerializeObject(outputExtensions));
}
private static List<Extension> GetExtensionList()
{
var extensionsJsonFileContent = FileUtility.ReadAllText(Settings.ExtensionsJsonFilePath);
return JsonConvert.DeserializeObject<List<Extension>>(extensionsJsonFileContent);
}
public static void CreateExtensionBundle(BundlePackageConfiguration bundlePackageConfig)
{
// Create a directory to hold the bundle content
string bundlePath = Path.Combine(Settings.RootBuildDirectory, bundlePackageConfig.BundleName);
foreach (var packageConfig in bundlePackageConfig.ConfigBinariesToInclude)
{
// find the build configuration matching the config id
var buildConfig = Settings.WindowsBuildConfigurations.FirstOrDefault(b => b.ConfigId == packageConfig) ??
Settings.LinuxBuildConfigurations.FirstOrDefault(b => b.ConfigId == packageConfig);
string targetBundleBinariesPath = Path.Combine(bundlePath, buildConfig.PublishBinDirectorySubPath);
// Copy binaries
FileUtility.CopyDirectory(buildConfig.PublishBinDirectoryPath, targetBundleBinariesPath);
string extensionJsonFilePath = Path.Join(targetBundleBinariesPath, Settings.ExtensionsJsonFileName);
AddBindingInfoToExtensionsJson(extensionJsonFilePath);
}
// Copy templates
var staticContentDirectory = Path.Combine(bundlePath, Settings.StaticContentDirectoryName);
FileUtility.CopyDirectory(Settings.StaticContentDirectoryPath, staticContentDirectory);
// Add bundle.json
CreateBundleJsonFile(bundlePath);
// Add Csproj file
string projectPath = Path.Combine(bundlePath, "extensions.csproj");
File.Copy(bundlePackageConfig.CsProjFilePath, projectPath);
FileUtility.EnsureDirectoryExists(Settings.ArtifactsDirectory);
ZipFile.CreateFromDirectory(bundlePath, bundlePackageConfig.GeneratedBundleZipFilePath, CompressionLevel.NoCompression, false);
}
public static void PackageNetCoreV3Bundle()
{
CreateExtensionBundle(Settings.BundlePackageNetCoreV3);
}
public static void PackageNetCoreV3BundlesLinux()
{
CreateExtensionBundle(Settings.BundlePackageNetCoreV3Linux);
}
public static void PackageNetCoreV3BundlesWindows()
{
CreateExtensionBundle(Settings.BundlePackageNetCoreV3Any);
CreateExtensionBundle(Settings.BundlePackageNetCoreWindows);
}
public static void AddBundleZipFile(string rootPath, BundlePackageConfiguration packageConfig)
{
string bundleZipDestinationPath = Path.Combine(rootPath, packageConfig.GeneratedBundleZipFileName);
FileUtility.CopyFile(packageConfig.GeneratedBundleZipFilePath, bundleZipDestinationPath);
}
public static void CreateRUPackage()
{
FileUtility.EnsureDirectoryExists(Settings.RUPackagePath);
ZipFile.ExtractToDirectory(Settings.BundlePackageNetCoreWindows.GeneratedBundleZipFilePath, Settings.RUPackagePath);
var RURootPackagePath = Directory.GetParent(Settings.RUPackagePath);
ZipFile.CreateFromDirectory(RURootPackagePath.FullName, Path.Combine(Settings.ArtifactsDirectory, $"{BundleConfiguration.Instance.ExtensionBundleId}.{BundleConfiguration.Instance.ExtensionBundleVersion}_RU_package.zip"), CompressionLevel.Optimal, false);
}
public static void CreateCDNStoragePackage()
{
foreach (var indexFileMetadata in Settings.IndexFiles)
{
string directoryPath = Path.Combine(Settings.RootBinDirectory, indexFileMetadata.IndexFileDirectory, BundleConfiguration.Instance.ExtensionBundleId);
FileUtility.EnsureDirectoryExists(directoryPath);
var bundleVersionDirectory = Path.Combine(directoryPath, BundleConfiguration.Instance.ExtensionBundleVersion);
var contentDirectory = Path.Combine(bundleVersionDirectory, Settings.StaticContentDirectoryName);
FileUtility.CopyDirectory(Settings.StaticContentDirectoryPath, contentDirectory);
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
// Generating v1 index file
var indexFile = GetIndexFile($"{indexFileMetadata.EndPointUrl}/public/ExtensionBundles/{indexFileMetadata.BundleId}/index.json");
indexFile.Add(BundleConfiguration.Instance.ExtensionBundleVersion);
var indexFilePath = Path.Combine(Settings.RootBinDirectory, indexFileMetadata.IndexFileDirectory, BundleConfiguration.Instance.ExtensionBundleId, Settings.IndexFileName);
FileUtility.Write(indexFilePath, JsonConvert.SerializeObject(indexFile));
AddBundleZipFile(bundleVersionDirectory, Settings.BundlePackageNetCoreV3);
// Add bundle.json
CreateBundleJsonFile(bundleVersionDirectory);
// Add Csproj file
string projectPath = Path.Combine(bundleVersionDirectory, "extensions.csproj");
File.Copy(Settings.BundlePackageNetCoreV3.CsProjFilePath, projectPath);
ZipFile.CreateFromDirectory(Path.Combine(Settings.RootBinDirectory, indexFileMetadata.IndexFileDirectory), Path.Combine(Settings.ArtifactsDirectory, $"{indexFileMetadata.IndexFileDirectory}.zip"), CompressionLevel.NoCompression, false);
}
}
public static void CreateCDNStoragePackageWindows()
{
foreach (var indexFileMetadata in Settings.IndexFiles)
{
string packageRootDirectoryPath = Path.Combine(Settings.RootBinDirectory, $"{indexFileMetadata.IndexFileDirectory}_windows");
string packageBundleDirectory = Path.Combine(packageRootDirectoryPath, BundleConfiguration.Instance.ExtensionBundleId, BundleConfiguration.Instance.ExtensionBundleVersion);
FileUtility.EnsureDirectoryExists(packageBundleDirectory);
AddBundleZipFile(packageBundleDirectory, Settings.BundlePackageNetCoreV3Any);
AddBundleZipFile(packageBundleDirectory, Settings.BundlePackageNetCoreWindows);
string packageZipFilePath = Path.Combine(Settings.ArtifactsDirectory, $"{indexFileMetadata.IndexFileDirectory}_windows.zip");
ZipFile.CreateFromDirectory(packageRootDirectoryPath, packageZipFilePath, CompressionLevel.NoCompression, false);
}
}
public static void CreateCDNStoragePackageLinux()
{
foreach (var indexFileMetadata in Settings.IndexFiles)
{
string packageRootDirectoryPath = Path.Combine(Settings.RootBinDirectory, $"{indexFileMetadata.IndexFileDirectory}_linux");
string packageBundleDirectory = Path.Combine(packageRootDirectoryPath, BundleConfiguration.Instance.ExtensionBundleId, BundleConfiguration.Instance.ExtensionBundleVersion);
FileUtility.EnsureDirectoryExists(packageBundleDirectory);
AddBundleZipFile(packageBundleDirectory, Settings.BundlePackageNetCoreV3Linux);
string packageZipFilePath = Path.Combine(Settings.ArtifactsDirectory, $"{indexFileMetadata.IndexFileDirectory}_linux.zip");
ZipFile.CreateFromDirectory(packageRootDirectoryPath, packageZipFilePath, CompressionLevel.NoCompression, false);
}
}
public static HashSet<string> GetIndexFile(string path)
{
using (var httpClient = new HttpClient())
{
var response = httpClient.GetAsync(path).Result;
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return new HashSet<string>();
}
return JsonConvert.DeserializeObject<HashSet<string>>(response.Content.ReadAsStringAsync().Result);
}
}
public static void CreateBundleJsonFile(string path)
{
var serializer = new JsonSerializerSettings();
serializer.NullValueHandling = NullValueHandling.Ignore;
Extension bundleInfo = new Extension()
{
Id = BundleConfiguration.Instance.ExtensionBundleId,
Version = BundleConfiguration.Instance.ExtensionBundleVersion
};
var fileContents = JsonConvert.SerializeObject(bundleInfo, serializer);
string bundleJsonPath = Path.Combine(path, "bundle.json");
FileUtility.Write(bundleJsonPath, fileContents);
}
}
}