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);
        }
Esempio n. 2
0
        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);
        }