private void ProcessJavascript( FunctionItem function, bool noCompile, bool noAssemblyValidation, string gitSha, string gitBranch, string buildConfiguration ) { if (noCompile) { return; } Console.WriteLine($"=> Building function {Settings.InfoColor}{function.Name}{Settings.ResetColor} [{function.Function.Runtime}]"); var buildFolder = Path.GetDirectoryName(function.Project); var hash = Directory.GetFiles(buildFolder, "*", SearchOption.AllDirectories).ComputeHashForFiles(file => Path.GetRelativePath(buildFolder, file)); var package = Path.Combine(Settings.OutputDirectory, $"function_{_builder.FullName}_{function.LogicalId}_{hash}.zip"); _existingPackages.Remove(package); CreatePackage(package, gitSha, gitBranch, buildFolder); _builder.AddArtifact($"{function.FullName}::PackageName", package); }
private void ProcessParameter(PackageItem parameter) { AtLocation("Package", () => { // check if a build command is present if (parameter.Build != null) { Console.WriteLine($"=> Building package {Settings.InfoColor}{parameter.FullName}{Settings.ResetColor}"); var commandAndArguments = parameter.Build.Split(' ', 2); if (!new ProcessLauncher(BuildEventsConfig).Execute( commandAndArguments[0], commandAndArguments[1], Settings.WorkingDirectory, Settings.VerboseLevel >= VerboseLevel.Detailed, ColorizeOutput )) { LogError("package build command failed"); return; } } // discover files to package var files = new List <KeyValuePair <string, string> >(); string folder; string filePattern; SearchOption searchOption; var packageFiles = Path.Combine(Settings.WorkingDirectory, parameter.Files); if ((packageFiles.EndsWith("/", StringComparison.Ordinal) || Directory.Exists(packageFiles))) { folder = Path.GetFullPath(packageFiles); filePattern = "*"; searchOption = SearchOption.AllDirectories; } else { folder = Path.GetDirectoryName(packageFiles); filePattern = Path.GetFileName(packageFiles); searchOption = SearchOption.TopDirectoryOnly; } if (Directory.Exists(folder)) { foreach (var filePath in Directory.GetFiles(folder, filePattern, searchOption)) { var relativeFilePathName = Path.GetRelativePath(folder, filePath); files.Add(new KeyValuePair <string, string>(relativeFilePathName, filePath)); } files = files.OrderBy(file => file.Key).ToList(); } else { LogError($"cannot find folder '{Path.GetRelativePath(Settings.WorkingDirectory, folder)}'"); return; } // compute hash for all files var fileValueToFileKey = files.ToDictionary(kv => kv.Value, kv => kv.Key); var hash = files.Select(kv => kv.Value).ComputeHashForFiles(file => fileValueToFileKey[file]); var package = Path.Combine(Settings.OutputDirectory, $"package_{_builder.FullName}_{parameter.LogicalId}_{hash}.zip"); // only build package if it doesn't exist if (!_existingPackages.Remove(package)) { // create zip package if (parameter.Build == null) { Console.WriteLine($"=> Building package {Settings.InfoColor}{parameter.FullName}{Settings.ResetColor}"); } new ZipTool(BuildEventsConfig).ZipWithExecutable(package, files); } _builder.AddArtifact($"{parameter.FullName}::PackageName", package); }); // local functions string ColorizeOutput(string line) => line.Contains(": error ", StringComparison.Ordinal) ? $"{Settings.ErrorColor}{line}{Settings.ResetColor}" : line.Contains(": warning ", StringComparison.Ordinal) ? $"{Settings.WarningColor}{line}{Settings.ResetColor}" : line; }
//--- Methods --- public void AddArtifact(string fullName, string artifact) => _builder.AddArtifact(fullName, artifact);
public static void ProcessScala( FunctionItem function, bool skipCompile, bool noAssemblyValidation, string gitSha, string gitBranch, string buildConfiguration, string outputDirectory, HashSet <string> existingPackages, string gitInfoFilename, ModuleBuilder builder ) { var showOutput = Settings.VerboseLevel >= VerboseLevel.Detailed; var scalaOutputJar = ScalaPackager.Process(function, skipCompile, noAssemblyValidation, gitSha, gitBranch, buildConfiguration, showOutput); // compute hash for zip contents string hash; using (var zipArchive = ZipFile.OpenRead(scalaOutputJar)) { using (var md5 = MD5.Create()) using (var hashStream = new CryptoStream(Stream.Null, md5, CryptoStreamMode.Write)) { foreach (var entry in zipArchive.Entries.OrderBy(e => e.FullName)) { // hash file path var filePathBytes = Encoding.UTF8.GetBytes(entry.FullName.Replace('\\', '/')); hashStream.Write(filePathBytes, 0, filePathBytes.Length); // hash file contents using (var stream = entry.Open()) { stream.CopyTo(hashStream); } } hashStream.FlushFinalBlock(); hash = md5.Hash.ToHexString(); } } // rename function package with hash var package = Path.Combine(outputDirectory, $"function_{builder.FullName}_{function.LogicalId}_{hash}.jar"); if (!existingPackages.Remove(package)) { File.Move(scalaOutputJar, package); // add git-info.json file using (var zipArchive = ZipFile.Open(package, ZipArchiveMode.Update)) { var entry = zipArchive.CreateEntry(gitInfoFilename); // Set RW-R--R-- permissions attributes on non-Windows operating system if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { entry.ExternalAttributes = 0b1_000_000_110_100_100 << 16; } using (var stream = entry.Open()) { stream.Write(Encoding.UTF8.GetBytes(JObject.FromObject(new ModuleManifestGitInfo { SHA = gitSha, Branch = gitBranch }).ToString(Formatting.None))); } } } else { File.Delete(scalaOutputJar); } // decompress project zip into temporary folder so we can add the 'GITSHAFILE' files builder.AddArtifact($"{function.FullName}::PackageName", package); }
private void ProcessDotNet( FunctionItem function, bool noCompile, bool noAssemblyValidation, string gitSha, string gitBranch, string buildConfiguration ) { function.Language = "csharp"; // check if AWS Lambda Tools extension is installed if (!CheckDotNetLambdaToolIsInstalled()) { return; } // read settings from project file var csproj = XDocument.Load(function.Project, LoadOptions.PreserveWhitespace); var mainPropertyGroup = csproj.Element("Project")?.Element("PropertyGroup"); var targetFramework = mainPropertyGroup?.Element("TargetFramework").Value; var rootNamespace = mainPropertyGroup?.Element("RootNamespace")?.Value; var projectName = mainPropertyGroup?.Element("AssemblyName")?.Value ?? Path.GetFileNameWithoutExtension(function.Project); // compile function project Console.WriteLine($"=> Building function {function.Name} [{targetFramework}, {buildConfiguration}]"); var projectDirectory = Path.Combine(Settings.WorkingDirectory, Path.GetFileNameWithoutExtension(function.Project)); var temporaryPackage = Path.Combine(Settings.OutputDirectory, $"function_{_builder.FullName}_{function.LogicalId}_temporary.zip"); // check if the project contains an obsolete AWS Lambda Tools extension: <DotNetCliToolReference Include="Amazon.Lambda.Tools"/> var obsoleteNodes = csproj.DescendantNodes() .Where(node => (node is XElement element) && (element.Name == "DotNetCliToolReference") && ((string)element.Attribute("Include") == "Amazon.Lambda.Tools") ) .ToList(); if (obsoleteNodes.Any()) { LogWarn($"removing obsolete AWS Lambda Tools extension from {Path.GetRelativePath(Settings.WorkingDirectory, function.Project)}"); foreach (var obsoleteNode in obsoleteNodes) { var parent = obsoleteNode.Parent; // remove obsolete node obsoleteNode.Remove(); // remove parent if no children are left if (!parent.Elements().Any()) { parent.Remove(); } } csproj.Save(function.Project); } // validate the project is using the most recent lambdasharp assembly references if (!noAssemblyValidation && function.HasAssemblyValidation) { var includes = csproj.Element("Project") ?.Descendants("PackageReference") .Where(elem => elem.Attribute("Include")?.Value.StartsWith("LambdaSharp", StringComparison.Ordinal) ?? false) ?? Enumerable.Empty <XElement>(); foreach (var include in includes) { var expectedVersion = (Settings.ToolVersion.Major == 0) ? VersionInfo.Parse($"{Settings.ToolVersion.Major}.{Settings.ToolVersion.Minor}.{Settings.ToolVersion.Patch ?? 0}{Settings.ToolVersion.Suffix}") : VersionInfo.Parse($"{Settings.ToolVersion.Major}.{Settings.ToolVersion.Minor}{Settings.ToolVersion.Suffix}"); var library = include.Attribute("Include").Value; var libraryVersionText = include.Attribute("Version")?.Value; if (libraryVersionText == null) { LogError($"csproj file is missing a version attribute in its assembly reference for {library} (expected version: '{expectedVersion}')"); } else if (libraryVersionText.EndsWith(".*", StringComparison.Ordinal)) { if (!VersionInfo.TryParse(libraryVersionText.Substring(0, libraryVersionText.Length - 2), out var libraryVersion)) { LogError($"csproj file contains an invalid wildcard version in its assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } else if (!libraryVersion.IsAssemblyCompatibleWith(expectedVersion)) { // check if we're compiling a conditional package reference in contributor mode if ((include.Attribute("Condition")?.Value != null) && (Environment.GetEnvironmentVariable("LAMBDASHARP") != null)) { // show error as warning instead since this package reference will not be used anyway LogWarn($"csproj file contains a mismatched assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } else { LogError($"csproj file contains a mismatched assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } } } else if (!VersionInfo.TryParse(libraryVersionText, out var libraryVersion)) { LogError($"csproj file contains an invalid version in its assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } else if (!libraryVersion.IsAssemblyCompatibleWith(expectedVersion)) { // check if we're compiling a conditional package reference in contributor mode if ((include.Attribute("Condition")?.Value != null) && (Environment.GetEnvironmentVariable("LAMBDASHARP") != null)) { LogWarn($"csproj file contains a mismatched assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } else { LogError($"csproj file contains a mismatched assembly reference for {library} (expected version: '{expectedVersion}', found: '{libraryVersionText}')"); } } } if (Settings.HasErrors) { return; } } if (noCompile) { return; } // build project with AWS dotnet CLI lambda tool if (!DotNetLambdaPackage(targetFramework, buildConfiguration, temporaryPackage, projectDirectory)) { LogError("'dotnet lambda package' command failed"); return; } // collect sources with invoke methods var mappings = Enumerable.Empty <ApiGatewayInvocationMapping>() .Union(function.Sources .OfType <RestApiSource>() .Where(source => source.Invoke != null) .Select(source => new ApiGatewayInvocationMapping { RestApi = $"{source.HttpMethod}:/{string.Join("/", source.Path)}", Method = source.Invoke, RestApiSource = source }) ) .Union(function.Sources .OfType <WebSocketSource>() .Where(source => source.Invoke != null) .Select(source => new ApiGatewayInvocationMapping { WebSocket = source.RouteKey, Method = source.Invoke, WebSocketSource = source }) ) .ToList(); // verify the function handler can be found in the compiled assembly var buildFolder = Path.Combine(projectDirectory, "bin", buildConfiguration, targetFramework, "publish"); if (function.HasHandlerValidation) { if (function.Function.Handler is string handler) { ValidateEntryPoint( buildFolder, handler ); } } // create request/response schemas for invocation methods if (!LambdaSharpCreateInvocationSchemas( function, buildFolder, rootNamespace, function.Function.Handler as string, mappings )) { LogError("'lash util create-invoke-methods-schema' command failed"); return; } // add api mappings JSON file(s) if (mappings.Any()) { using (var zipArchive = ZipFile.Open(temporaryPackage, ZipArchiveMode.Update)) { var entry = zipArchive.CreateEntry(API_MAPPINGS); // Set RW-R--R-- permissions attributes on non-Windows operating system if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { entry.ExternalAttributes = 0b1_000_000_110_100_100 << 16; } using (var stream = entry.Open()) { stream.Write(Encoding.UTF8.GetBytes(JObject.FromObject(new ApiGatewayInvocationMappings { Mappings = mappings }).ToString(Formatting.None))); } } } // compute hash for zip contents string hash; using (var zipArchive = ZipFile.OpenRead(temporaryPackage)) { using (var md5 = MD5.Create()) using (var hashStream = new CryptoStream(Stream.Null, md5, CryptoStreamMode.Write)) { foreach (var entry in zipArchive.Entries.OrderBy(e => e.FullName)) { // hash file path var filePathBytes = Encoding.UTF8.GetBytes(entry.FullName.Replace('\\', '/')); hashStream.Write(filePathBytes, 0, filePathBytes.Length); // hash file contents using (var stream = entry.Open()) { stream.CopyTo(hashStream); } } hashStream.FlushFinalBlock(); hash = md5.Hash.ToHexString(); } } // rename function package with hash var package = Path.Combine(Settings.OutputDirectory, $"function_{_builder.FullName}_{function.LogicalId}_{hash}.zip"); if (!_existingPackages.Remove(package)) { File.Move(temporaryPackage, package); // add git-info.json file using (var zipArchive = ZipFile.Open(package, ZipArchiveMode.Update)) { var entry = zipArchive.CreateEntry(GIT_INFO_FILE); // Set RW-R--R-- permissions attributes on non-Windows operating system if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { entry.ExternalAttributes = 0b1_000_000_110_100_100 << 16; } using (var stream = entry.Open()) { stream.Write(Encoding.UTF8.GetBytes(JObject.FromObject(new ModuleManifestGitInfo { SHA = gitSha, Branch = gitBranch }).ToString(Formatting.None))); } } } else { File.Delete(temporaryPackage); } // set the module variable to the final package name _builder.AddArtifact($"{function.FullName}::PackageName", package); }