public PackageCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, PackageCommandOptions, args)
 {
 }
        /// <summary>
        /// Check to see if any of the dependencies listed in the deps.json file are pulling in later version of NETStandard.Library
        /// then the target framework supports.
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="targetFramework"></param>
        /// <param name="depsJsonTargetNode"></param>
        /// <returns></returns>
        private static bool ValidateDependencies(IToolLogger logger, string targetFramework, JsonData depsJsonTargetNode)
        {
            Version maxNETStandardLibraryVersion;

            // If we don't know the NETStandard.Library NuGet package version then skip validation. This is to handle
            // the case we are packaging up for a future target framework verion then this version of the tooling knows about.
            // Skip validation so the tooling doesn't get in the way.
            if (!NETSTANDARD_LIBRARY_VERSIONS.TryGetValue(targetFramework, out maxNETStandardLibraryVersion))
            {
                return(true);
            }

            var     dependenciesUsingNETStandard = new List <string>();
            Version referencedNETStandardLibrary = null;

            foreach (KeyValuePair <string, JsonData> dependencyNode in depsJsonTargetNode)
            {
                var nameAndVersion = dependencyNode.Key.Split('/');
                if (nameAndVersion.Length != 2)
                {
                    continue;
                }

                if (string.Equals(nameAndVersion[0], "netstandard.library", StringComparison.OrdinalIgnoreCase))
                {
                    if (!Version.TryParse(nameAndVersion[1], out referencedNETStandardLibrary))
                    {
                        logger.WriteLine($"Error parsing version number for declared NETStandard.Library: {nameAndVersion[1]}");
                        return(true);
                    }
                }
                // Collect the dependencies that are pulling in the NETStandard.Library metapackage
                else
                {
                    var subDependencies = dependencyNode.Value["dependencies"] as JsonData;
                    if (subDependencies != null)
                    {
                        foreach (KeyValuePair <string, JsonData> subDependency in subDependencies)
                        {
                            if (string.Equals(subDependency.Key, "netstandard.library", StringComparison.OrdinalIgnoreCase))
                            {
                                dependenciesUsingNETStandard.Add(nameAndVersion[0] + " : " + nameAndVersion[1]);
                                break;
                            }
                        }
                    }
                }
            }

            // If true the project is pulling in a new version of NETStandard.Library then the target framework supports.
            if (referencedNETStandardLibrary != null && maxNETStandardLibraryVersion < referencedNETStandardLibrary)
            {
                logger?.WriteLine($"Error: Project is referencing NETStandard.Library version {referencedNETStandardLibrary.ToString()}. Max version supported by {targetFramework} is {maxNETStandardLibraryVersion.ToString()}.");

                // See if we can find the target framework that does support the version the project is pulling in.
                // This can help the user know what framework their dependencies are targeting instead of understanding NuGet version numbers.
                var matchingTargetFramework = NETSTANDARD_LIBRARY_VERSIONS.FirstOrDefault(x =>
                {
                    return(x.Value.Equals(referencedNETStandardLibrary));
                });

                if (!string.IsNullOrEmpty(matchingTargetFramework.Key))
                {
                    logger?.WriteLine($"Error: NETStandard.Library {referencedNETStandardLibrary.ToString()} is used for target framework {matchingTargetFramework.Key}.");
                }

                if (dependenciesUsingNETStandard.Count != 0)
                {
                    logger?.WriteLine($"Error: Check the following dependencies for versions compatible with {targetFramework}:");
                    foreach (var dependency in dependenciesUsingNETStandard)
                    {
                        logger?.WriteLine($"Error: \t{dependency}");
                    }
                }

                return(false);
            }


            return(true);
        }
Beispiel #3
0
 public BaseCommand(IToolLogger logger, string workingDirectory)
 {
     this.Logger           = logger;
     this.WorkingDirectory = workingDirectory;
 }
        /// <summary>
        /// Execute the dotnet publish command and zip up the resulting publish folder.
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="logger"></param>
        /// <param name="workingDirectory"></param>
        /// <param name="projectLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="msbuildParameters"></param>
        /// <param name="disableVersionCheck"></param>
        /// <param name="publishLocation"></param>
        /// <param name="zipArchivePath"></param>
        public static bool CreateApplicationBundle(LambdaToolsDefaults defaults, IToolLogger logger, string workingDirectory,
                                                   string projectLocation, string configuration, string targetFramework, string msbuildParameters, bool disableVersionCheck,
                                                   out string publishLocation, ref string zipArchivePath)
        {
            if (string.IsNullOrEmpty(configuration))
            {
                configuration = LambdaConstants.DEFAULT_BUILD_CONFIGURATION;
            }

            string lambdaRuntimePackageStoreManifestContent = null;
            var    computedProjectLocation = Utilities.DetermineProjectLocation(workingDirectory, projectLocation);

            var packageStoreManifest = LambdaUtilities.LoadPackageStoreManifest(logger, targetFramework);

            if (!disableVersionCheck)
            {
                LambdaUtilities.ValidateMicrosoftAspNetCoreAllReferenceFromProjectPath(logger, targetFramework, packageStoreManifest, computedProjectLocation);
            }

            var cli = new LambdaDotNetCLIWrapper(logger, workingDirectory);

            publishLocation = Utilities.DeterminePublishLocation(workingDirectory, projectLocation, configuration, targetFramework);
            logger?.WriteLine("Executing publish command");
            if (cli.Publish(defaults, projectLocation, publishLocation, targetFramework, configuration, msbuildParameters, lambdaRuntimePackageStoreManifestContent) != 0)
            {
                return(false);
            }

            var buildLocation = Utilities.DetermineBuildLocation(workingDirectory, projectLocation, configuration, targetFramework);

            // This is here for legacy reasons. Some older versions of the dotnet CLI were not
            // copying the deps.json file into the publish folder.
            foreach (var file in Directory.GetFiles(buildLocation, "*.deps.json", SearchOption.TopDirectoryOnly))
            {
                var destinationPath = Path.Combine(publishLocation, Path.GetFileName(file));
                if (!File.Exists(destinationPath))
                {
                    File.Copy(file, destinationPath);
                }
            }

            bool flattenRuntime     = false;
            var  depsJsonTargetNode = GetDepsJsonTargetNode(logger, publishLocation);

            // If there is no target node then this means the tool is being used on a future version of .NET Core
            // then was available when the this tool was written. Go ahead and continue the deployment with warnings so the
            // user can see if the future version will work.
            if (depsJsonTargetNode != null && string.Equals(targetFramework, "netcoreapp1.0", StringComparison.OrdinalIgnoreCase))
            {
                // Make sure the project is not pulling in dependencies requiring a later version of .NET Core then the declared target framework
                if (!ValidateDependencies(logger, targetFramework, depsJsonTargetNode, disableVersionCheck))
                {
                    return(false);
                }

                // Flatten the runtime folder which reduces the package size by not including native dependencies
                // for other platforms.
                flattenRuntime = FlattenRuntimeFolder(logger, publishLocation, depsJsonTargetNode);
            }

            if (zipArchivePath == null)
            {
                zipArchivePath = Path.Combine(Directory.GetParent(publishLocation).FullName, new DirectoryInfo(computedProjectLocation).Name + ".zip");
            }

            zipArchivePath = Path.GetFullPath(zipArchivePath);
            logger?.WriteLine($"Zipping publish folder {publishLocation} to {zipArchivePath}");
            if (File.Exists(zipArchivePath))
            {
                File.Delete(zipArchivePath);
            }

            var zipArchiveParentDirectory = Path.GetDirectoryName(zipArchivePath);

            if (!Directory.Exists(zipArchiveParentDirectory))
            {
                logger?.WriteLine($"Creating directory {zipArchiveParentDirectory}");
                new DirectoryInfo(zipArchiveParentDirectory).Create();
            }


            BundleDirectory(zipArchivePath, publishLocation, flattenRuntime, logger);

            return(true);
        }
 public DeleteServerlessCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, DeleteCommandOptions, args)
 {
 }
Beispiel #6
0
        /// <summary>
        /// Make sure that if the project references the Microsoft.AspNetCore.All package which is in implicit package store
        /// that the Lambda runtime has that store available. Otherwise the Lambda function will fail with an Internal server error.
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="manifestContent"></param>
        /// <param name="projContent"></param>
        public static void ValidateMicrosoftAspNetCoreAllReferenceFromProjectContent(IToolLogger logger, string targetFramework, string manifestContent, string projContent)
        {
            const string NO_VERSION      = "NO_VERSION";
            const string ASPNET_CORE_ALL = "Microsoft.AspNetCore.All";
            const string ASPNET_CORE_APP = "Microsoft.AspNetCore.App";

            try
            {
                XDocument projXmlDoc = XDocument.Parse(projContent);

                Func <string, string> searchForPackageVersion = (nuGetPackage) =>
                {
                    // Not using XPath because to avoid adding an addition dependency for a simple one time use.
                    foreach (var group in projXmlDoc.Root.Elements("ItemGroup"))
                    {
                        foreach (XElement packageReference in group.Elements("PackageReference"))
                        {
                            var name = packageReference.Attribute("Include")?.Value;
                            if (string.Equals(name, nuGetPackage, StringComparison.Ordinal))
                            {
                                return(packageReference.Attribute("Version")?.Value ?? NO_VERSION);
                            }
                        }
                    }

                    return(null);
                };

                Func <string, string, Tuple <bool, string> > searchForSupportedVersion = (nuGetPackage, nuGetPackageVersion) =>
                {
                    if (string.IsNullOrEmpty(manifestContent))
                    {
                        return(new Tuple <bool, string>(true, null));
                    }

                    var manifestXmlDoc = XDocument.Parse(manifestContent);

                    string latestLambdaDeployedVersion = null;
                    foreach (var element in manifestXmlDoc.Root.Elements("Package"))
                    {
                        var name = element.Attribute("Id")?.Value;
                        if (string.Equals(name, nuGetPackage, StringComparison.Ordinal))
                        {
                            var version = element.Attribute("Version")?.Value;
                            if (string.Equals(nuGetPackageVersion, version, StringComparison.Ordinal))
                            {
                                // Version specifed in project file is available in Lambda Runtime
                                return(new Tuple <bool, string>(true, null));
                            }

                            // Record latest supported version to provide meaningful error message.
                            if (latestLambdaDeployedVersion == null || Version.Parse(latestLambdaDeployedVersion) < Version.Parse(version))
                            {
                                latestLambdaDeployedVersion = version;
                            }
                        }
                    }

                    return(new Tuple <bool, string>(false, latestLambdaDeployedVersion));
                };

                var projectAspNetCoreAllVersion = searchForPackageVersion(ASPNET_CORE_ALL);
                var projectAspNetCoreAppVersion = searchForPackageVersion(ASPNET_CORE_APP);

                if (string.IsNullOrEmpty(projectAspNetCoreAllVersion) && string.IsNullOrEmpty(projectAspNetCoreAppVersion))
                {
                    // Project is not using Microsoft.AspNetCore.All so skip validation.
                    return;
                }


                if (string.Equals("netcoreapp2.0", targetFramework, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(projectAspNetCoreAllVersion))
                {
                    if (string.IsNullOrEmpty(manifestContent))
                    {
                        return;
                    }

                    var results = searchForSupportedVersion(ASPNET_CORE_ALL, projectAspNetCoreAllVersion);
                    // project specified version is supported.
                    if (results.Item1)
                    {
                        return;
                    }

                    throw new LambdaToolsException($"Project is referencing version {projectAspNetCoreAllVersion} of {ASPNET_CORE_ALL} which is newer " +
                                                   $"than {results.Item2}, the latest version available in the Lambda Runtime environment. Please update your project to " +
                                                   $"use version {results.Item2} and then redeploy your Lambda function.",
                                                   LambdaToolsException.LambdaErrorCode.AspNetCoreAllValidation);
                }
                else if (string.Equals("netcoreapp2.1", targetFramework, StringComparison.OrdinalIgnoreCase))
                {
                    string packageName, packageVersion;
                    if (projectAspNetCoreAllVersion != null)
                    {
                        packageName    = ASPNET_CORE_ALL;
                        packageVersion = projectAspNetCoreAllVersion;
                    }
                    else
                    {
                        packageName    = ASPNET_CORE_APP;
                        packageVersion = projectAspNetCoreAppVersion;
                    }

                    var results = searchForSupportedVersion(packageName, packageVersion);

                    // When .NET Core 2.1 was first released developers were encouraged to not include a version attribute for Microsoft.AspNetCore.All or Microsoft.AspNetCore.App.
                    // This turns out not to be a good practice for Lambda because it makes the package bundle require the latest version of these packages
                    // that is installed on the dev/build box regardless of what is supported in Lambda. To avoid deployment failure confusion we will require the version attribute
                    // be set so we can verify compatiblity.
                    if (string.Equals(packageVersion, NO_VERSION, StringComparison.OrdinalIgnoreCase))
                    {
                        var message = $"Project is referencing {packageName} without specifying a version. A version is required to ensure compatiblity with the supported versions of {packageName} " +
                                      $"in the Lambda compute environment. Edit the PackageReference for {packageName} in your project file to include a Version attribute.";

                        if (!string.IsNullOrEmpty(results.Item2))
                        {
                            message += "  The latest version supported in Lambda is {results.Item2}.";
                        }
                        throw new LambdaToolsException(message,
                                                       LambdaToolsException.LambdaErrorCode.AspNetCoreAllValidation);
                    }

                    // project specified version is supported.
                    if (results.Item1)
                    {
                        return;
                    }


                    if (packageVersion.StartsWith("2.0", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new LambdaToolsException($"Project is referencing version {packageVersion} of {packageName}. The minimum supported version is 2.1.0 for the .NET Core 2.1 Lambda runtime.",
                                                       LambdaToolsException.LambdaErrorCode.AspNetCoreAllValidation);
                    }

                    throw new LambdaToolsException($"Project is referencing version {packageVersion} of {packageName} which is newer " +
                                                   $"than {results.Item2}, the latest version available in the Lambda Runtime environment. Please update your project to " +
                                                   $"use version {results.Item2} and then redeploy your Lambda function.",
                                                   LambdaToolsException.LambdaErrorCode.AspNetCoreAllValidation);
                }
            }
            catch (LambdaToolsException)
            {
                throw;
            }
            catch (Exception e)
            {
                logger?.WriteLine($"Unknown error validating version of {ASPNET_CORE_ALL}: {e.Message}");
            }
        }
Beispiel #7
0
 public DotNetCLIWrapper(IToolLogger logger, string workingDirectory)
     : base(logger, workingDirectory)
 {
 }
Beispiel #8
0
        private static EventHandler <StreamTransferProgressArgs> CreateProgressHandler(IToolLogger logger)
        {
            int percentToUpdateOn = UPLOAD_PROGRESS_INCREMENT;
            EventHandler <StreamTransferProgressArgs> handler = ((s, e) =>
            {
                if (e.PercentDone == percentToUpdateOn || e.PercentDone > percentToUpdateOn)
                {
                    int increment = e.PercentDone % UPLOAD_PROGRESS_INCREMENT;
                    if (increment == 0)
                    {
                        increment = UPLOAD_PROGRESS_INCREMENT;
                    }
                    percentToUpdateOn = e.PercentDone + increment;
                    logger.WriteLine($"... Progress: {e.PercentDone}%");
                }
            });

            return(handler);
        }
        /// <summary>
        /// Creates the deployment bundle using the native zip tool installed
        /// on the system (default /usr/bin/zip). This is what is typically used on Linux and OSX
        /// </summary>
        /// <param name="zipCLI">The path to the located zip binary.</param>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="rootDirectory">The root directory where all of the relative paths in includedFiles is pointing to.</param>
        /// <param name="includedFiles">Map of relative to absolute path of files to include in bundle.</param>
        /// <param name="logger">Logger instance.</param>
        private static void BundleWithZipCLI(string zipCLI, string zipArchivePath, string rootDirectory, IDictionary <string, string> includedFiles, IToolLogger logger)
        {
            var args = new StringBuilder("\"" + zipArchivePath + "\"");

            foreach (var kvp in includedFiles)
            {
                args.AppendFormat(" \"{0}\"", kvp.Key);
            }

            var psiZip = new ProcessStartInfo
            {
                FileName               = zipCLI,
                Arguments              = args.ToString(),
                WorkingDirectory       = rootDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

            var handler = (DataReceivedEventHandler)((o, e) =>
            {
                if (string.IsNullOrEmpty(e.Data))
                {
                    return;
                }
                logger?.WriteLine("... zipping: " + e.Data);
            });

            using (var proc = new Process())
            {
                proc.StartInfo = psiZip;
                proc.Start();

                proc.ErrorDataReceived  += handler;
                proc.OutputDataReceived += handler;
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();

                proc.EnableRaisingEvents = true;
                proc.WaitForExit();

                if (proc.ExitCode == 0)
                {
                    logger?.WriteLine(string.Format("Created publish archive ({0}).", zipArchivePath));
                }
            }
        }
 public PushDockerImageCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, CommandOptions, args)
 {
 }
        /// <summary>
        /// Creates the deployment bundle using the native zip tool installed
        /// on the system (default /usr/bin/zip). This is what is typically used on Linux and OSX
        /// </summary>
        /// <param name="zipCLI">The path to the located zip binary.</param>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="publishLocation">The location to be bundled.</param>
        /// <param name="flattenRuntime">If true the runtimes folder will be flatten</param>
        /// <param name="logger">Logger instance.</param>
        private static void BundleWithZipCLI(string zipCLI, string zipArchivePath, string publishLocation, bool flattenRuntime, IToolLogger logger)
        {
            var allFiles = GetFilesToIncludeInArchive(publishLocation, flattenRuntime);

            BundleWithZipCLI(zipCLI, zipArchivePath, publishLocation, allFiles, logger);
        }
        /// <summary>
        /// Zip up the publish folder using the .NET compression libraries. This is what is used when run on Windows.
        /// </summary>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="rootDirectory">The root directory where all of the relative paths in includedFiles is pointing to.</param>
        /// <param name="includedFiles">Map of relative to absolute path of files to include in bundle.</param>
        /// <param name="logger">Logger instance.</param>
        private static void BundleWithDotNetCompression(string zipArchivePath, string rootDirectory, IDictionary <string, string> includedFiles, IToolLogger logger)
        {
            using (var zipArchive = ZipFile.Open(zipArchivePath, ZipArchiveMode.Create))
            {
                foreach (var kvp in includedFiles)
                {
                    zipArchive.CreateEntryFromFile(kvp.Value, kvp.Key);

                    logger?.WriteLine($"... zipping: {kvp.Key}");
                }
            }
        }
        /// <summary>
        /// Zip up the publish folder using the .NET compression libraries. This is what is used when run on Windows.
        /// </summary>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="publishLocation">The location to be bundled.</param>
        /// <param name="flattenRuntime">If true the runtimes folder will be flatten</param>
        /// <param name="logger">Logger instance.</param>
        private static void BundleWithDotNetCompression(string zipArchivePath, string publishLocation, bool flattenRuntime, IToolLogger logger)
        {
            var includedFiles = GetFilesToIncludeInArchive(publishLocation, flattenRuntime);

            BundleWithDotNetCompression(zipArchivePath, publishLocation, includedFiles, logger);
        }
        /// <summary>
        /// Zip up the publish folder using the .NET compression libraries. This is what is used when run on Windows.
        /// </summary>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="publishLocation">The location to be bundled.</param>
        /// <param name="flattenRuntime">If true the runtimes folder will be flatten</param>
        /// <param name="logger">Logger instance.</param>
        private static void BundleWithDotNetCompression(string zipArchivePath, string publishLocation, bool flattenRuntime, IToolLogger logger)
        {
            using (var zipArchive = ZipFile.Open(zipArchivePath, ZipArchiveMode.Create))
            {
                var includedFiles = GetFilesToIncludeInArchive(publishLocation, flattenRuntime);
                foreach (var kvp in includedFiles)
                {
                    zipArchive.CreateEntryFromFile(kvp.Value, kvp.Key);

                    logger?.WriteLine($"... zipping: {kvp.Key}");
                }
            }
        }
        /// <summary>
        /// Execute the dotnet publish command and zip up the resulting publish folder.
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="logger"></param>
        /// <param name="workingDirectory"></param>
        /// <param name="projectLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="disableVersionCheck"></param>
        /// <param name="publishLocation"></param>
        /// <param name="zipArchivePath"></param>
        public static bool CreateApplicationBundle(LambdaToolsDefaults defaults, IToolLogger logger, string workingDirectory,
                                                   string projectLocation, string configuration, string targetFramework, string msbuildParameters, bool disableVersionCheck,
                                                   out string publishLocation, ref string zipArchivePath)
        {
            string lambdaRuntimePackageStoreManifestContent = null;

            if (!disableVersionCheck)
            {
                Utilities.ValidateMicrosoftAspNetCoreAllReference(logger, Utilities.DetermineProjectLocation(workingDirectory, projectLocation), out lambdaRuntimePackageStoreManifestContent);
            }

            var cli = new DotNetCLIWrapper(logger, workingDirectory);

            publishLocation = Utilities.DeterminePublishLocation(workingDirectory, projectLocation, configuration, targetFramework);
            logger?.WriteLine("Executing publish command");
            if (cli.Publish(defaults, projectLocation, publishLocation, targetFramework, configuration, msbuildParameters, lambdaRuntimePackageStoreManifestContent) != 0)
            {
                return(false);
            }

            var buildLocation = Utilities.DetermineBuildLocation(workingDirectory, projectLocation, configuration, targetFramework);

            // This is here for legacy reasons. Some older versions of the dotnet CLI were not
            // copying the deps.json file into the publish folder.
            foreach (var file in Directory.GetFiles(buildLocation, "*.deps.json", SearchOption.TopDirectoryOnly))
            {
                var destinationPath = Path.Combine(publishLocation, Path.GetFileName(file));
                if (!File.Exists(destinationPath))
                {
                    File.Copy(file, destinationPath);
                }
            }

            bool flattenRuntime     = false;
            var  depsJsonTargetNode = GetDepsJsonTargetNode(logger, publishLocation);

            // If there is no target node then this means the tool is being used on a future version of .NET Core
            // then was available when the this tool was written. Go ahead and continue the deployment with warnings so the
            // user can see if the future version will work.
            if (depsJsonTargetNode != null && string.Equals(targetFramework, "netcoreapp1.0", StringComparison.OrdinalIgnoreCase))
            {
                // Make sure the project is not pulling in dependencies requiring a later version of .NET Core then the declared target framework
                if (!ValidateDependencies(logger, targetFramework, depsJsonTargetNode, disableVersionCheck))
                {
                    return(false);
                }

                // Flatten the runtime folder which reduces the package size by not including native dependencies
                // for other platforms.
                flattenRuntime = FlattenRuntimeFolder(logger, publishLocation, depsJsonTargetNode);
            }

            if (zipArchivePath == null)
            {
                zipArchivePath = Path.Combine(Directory.GetParent(publishLocation).FullName, new DirectoryInfo(workingDirectory).Name + ".zip");
            }

            zipArchivePath = Path.GetFullPath(zipArchivePath);
            logger?.WriteLine($"Zipping publish folder {publishLocation} to {zipArchivePath}");
            if (File.Exists(zipArchivePath))
            {
                File.Delete(zipArchivePath);
            }

            var zipArchiveParentDirectory = Path.GetDirectoryName(zipArchivePath);

            if (!Directory.Exists(zipArchiveParentDirectory))
            {
                logger?.WriteLine($"Creating directory {zipArchiveParentDirectory}");
                new DirectoryInfo(zipArchiveParentDirectory).Create();
            }


#if NETCORE
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                BundleWithDotNetCompression(zipArchivePath, publishLocation, flattenRuntime, logger);
            }
            else
            {
                // Use the native zip utility if it exist which will maintain linux/osx file permissions
                var zipCLI = DotNetCLIWrapper.FindExecutableInPath("zip");
                if (!string.IsNullOrEmpty(zipCLI))
                {
                    BundleWithZipCLI(zipCLI, zipArchivePath, publishLocation, flattenRuntime, logger);
                }
                else
                {
                    throw new LambdaToolsException("Failed to find the \"zip\" utility program in path. This program is required to maintain Linux file permissions in the zip archive.", LambdaToolsException.ErrorCode.FailedToFindZipProgram);
                }
            }
#else
            BundleWithDotNetCompression(zipArchivePath, publishLocation, flattenRuntime, logger);
#endif



            return(true);
        }
        /// <summary>
        /// Execute the dotnet publish command and zip up the resulting publish folder.
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="logger"></param>
        /// <param name="workingDirectory"></param>
        /// <param name="projectLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="publishLocation"></param>
        /// <param name="zipArchivePath"></param>
        public static bool CreateApplicationBundle(LambdaToolsDefaults defaults, IToolLogger logger, string workingDirectory, string projectLocation, string configuration, string targetFramework,
                                                   out string publishLocation, ref string zipArchivePath)
        {
            var cli = new DotNetCLIWrapper(logger, workingDirectory);

            publishLocation = Utilities.DeterminePublishLocation(workingDirectory, projectLocation, configuration, targetFramework);
            logger?.WriteLine("Executing publish command");
            if (cli.Publish(defaults, projectLocation, publishLocation, targetFramework, configuration) != 0)
            {
                return(false);
            }

            var buildLocation = Utilities.DetermineBuildLocation(workingDirectory, projectLocation, configuration, targetFramework);

            // This is here for legacy reasons. Some older versions of the dotnet CLI were not
            // copying the deps.json file into the publish folder.
            foreach (var file in Directory.GetFiles(buildLocation, "*.deps.json", SearchOption.TopDirectoryOnly))
            {
                var destinationPath = Path.Combine(publishLocation, Path.GetFileName(file));
                if (!File.Exists(destinationPath))
                {
                    File.Copy(file, destinationPath);
                }
            }

            bool flattenRuntime     = false;
            var  depsJsonTargetNode = GetDepsJsonTargetNode(logger, publishLocation);

            // If there is no target node then this means the tool is being used on a future version of .NET Core
            // then was available when the this tool was written. Go ahead and continue the deployment with warnings so the
            // user can see if the future version will work.
            if (depsJsonTargetNode != null)
            {
                // Make sure the project is not pulling in dependencies requiring a later version of .NET Core then the declared target framework
                if (!ValidateDependencies(logger, targetFramework, depsJsonTargetNode))
                {
                    return(false);
                }

                // Flatten the runtime folder which reduces the package size by not including native dependencies
                // for other platforms.
                flattenRuntime = FlattenRuntimeFolder(logger, publishLocation, depsJsonTargetNode);
            }

            if (zipArchivePath == null)
            {
                zipArchivePath = Path.Combine(Directory.GetParent(publishLocation).FullName, new DirectoryInfo(workingDirectory).Name + ".zip");
            }

            zipArchivePath = Path.GetFullPath(zipArchivePath);
            logger?.WriteLine($"Zipping publish folder {publishLocation} to {zipArchivePath}");
            if (File.Exists(zipArchivePath))
            {
                File.Delete(zipArchivePath);
            }

            var zipArchiveParentDirectory = Path.GetDirectoryName(zipArchivePath);

            if (!Directory.Exists(zipArchiveParentDirectory))
            {
                logger?.WriteLine($"Creating directory {zipArchiveParentDirectory}");
                new DirectoryInfo(zipArchiveParentDirectory).Create();
            }

            // Use the native zip utility if it exist which will maintain linux/osx file permissions
            var zipCLI = DotNetCLIWrapper.FindExecutableInPath("zip");

            if (!string.IsNullOrEmpty(zipCLI))
            {
                BundleWithZipCLI(zipCLI, zipArchivePath, publishLocation, flattenRuntime, logger);
            }
            else
            {
                BundleWithDotNetCompression(zipArchivePath, publishLocation, flattenRuntime, logger);
            }

            return(true);
        }
 public ECSBaseCommand(IToolLogger logger, string workingDirectory)
     : base(logger, workingDirectory)
 {
 }
Beispiel #18
0
        /// <summary>
        /// Creates the deployment bundle using the native zip tool installed
        /// on the system (default /usr/bin/zip).
        /// </summary>
        /// <param name="zipCLI">The path to the located zip binary.</param>
        /// <param name="zipArchivePath">The path and name of the zip archive to create.</param>
        /// <param name="publishLocation">The location to be bundled.</param>
        /// <param name="logger">Optional logger instance.</param>
        private static void BundleWithZipCLI(string zipCLI, string zipArchivePath, string publishLocation, IToolLogger logger)
        {
            var args = new StringBuilder("\"" + zipArchivePath + "\"");

            // so that we can archive content in subfolders, take the length of the
            // path to the root publish location and we'll just substring the
            // found files so the subpaths are retained
            var publishRootLength = publishLocation.Length;

            if (publishLocation[publishRootLength - 1] != Path.DirectorySeparatorChar)
            {
                publishRootLength++;
            }

            var allFiles = Directory.GetFiles(publishLocation, "*.*", SearchOption.AllDirectories);

            foreach (var f in allFiles)
            {
                args.AppendFormat(" \"{0}\"", f.Substring(publishRootLength));
            }

            var psiZip = new ProcessStartInfo
            {
                FileName               = zipCLI,
                Arguments              = args.ToString(),
                WorkingDirectory       = publishLocation,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

            var handler = (DataReceivedEventHandler)((o, e) =>
            {
                if (string.IsNullOrEmpty(e.Data))
                {
                    return;
                }
                logger?.WriteLine("... publish: " + e.Data);
            });

            using (var proc = new Process())
            {
                proc.StartInfo = psiZip;
                proc.Start();

                proc.ErrorDataReceived  += handler;
                proc.OutputDataReceived += handler;
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();

                proc.EnableRaisingEvents = true;
                proc.WaitForExit();

                if (proc.ExitCode == 0)
                {
                    logger?.WriteLine(string.Format("Created publish archive ({0}).", zipArchivePath));
                }
            }
        }
 public ECSBaseCommand(IToolLogger logger, string workingDirectory, IList <CommandOption> possibleOptions, string[] args)
     : base(logger, workingDirectory, possibleOptions, args)
 {
 }
Beispiel #20
0
 public ListServerlessCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, ListCommandOptions, args)
 {
 }
Beispiel #21
0
 public DeployFunctionCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, DeployCommandOptions, args)
 {
 }
Beispiel #22
0
        public static string ProcessTemplateSubstitions(IToolLogger logger, string templateBody, IDictionary <string, string> substitutions, string workingDirectory)
        {
            if (DetermineTemplateFormat(templateBody) != TemplateFormat.Json || substitutions == null || !substitutions.Any())
            {
                return(templateBody);
            }

            logger?.WriteLine($"Processing {substitutions.Count} substitutions.");
            var root = JsonConvert.DeserializeObject(templateBody) as JObject;

            foreach (var kvp in substitutions)
            {
                logger?.WriteLine($"Processing substitution: {kvp.Key}");
                var token = root.SelectToken(kvp.Key);
                if (token == null)
                {
                    throw new LambdaToolsException($"Failed to locate JSONPath {kvp.Key} for template substitution.", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                }

                logger?.WriteLine($"\tFound element of type {token.Type}");

                string replacementValue;
                if (workingDirectory != null && File.Exists(Path.Combine(workingDirectory, kvp.Value)))
                {
                    var path = Path.Combine(workingDirectory, kvp.Value);
                    logger?.WriteLine($"\tReading: {path}");
                    replacementValue = File.ReadAllText(path);
                }
                else
                {
                    replacementValue = kvp.Value;
                }

                try
                {
                    switch (token.Type)
                    {
                    case JTokenType.String:
                        ((JValue)token).Value = replacementValue;
                        break;

                    case JTokenType.Boolean:
                        bool b;
                        if (bool.TryParse(replacementValue, out b))
                        {
                            ((JValue)token).Value = b;
                        }
                        else
                        {
                            throw new LambdaToolsException($"Failed to convert {replacementValue} to a bool", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                        }

                        break;

                    case JTokenType.Integer:
                        int i;
                        if (int.TryParse(replacementValue, out i))
                        {
                            ((JValue)token).Value = i;
                        }
                        else
                        {
                            throw new LambdaToolsException($"Failed to convert {replacementValue} to an int", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                        }
                        break;

                    case JTokenType.Float:
                        double d;
                        if (double.TryParse(replacementValue, out d))
                        {
                            ((JValue)token).Value = d;
                        }
                        else
                        {
                            throw new LambdaToolsException($"Failed to convert {replacementValue} to a double", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                        }
                        break;

                    case JTokenType.Array:
                    case JTokenType.Object:
                        var    jcon  = token as JContainer;
                        var    jprop = jcon.Parent as JProperty;
                        JToken subData;
                        try
                        {
                            subData = JsonConvert.DeserializeObject(replacementValue) as JToken;
                        }
                        catch (Exception e)
                        {
                            throw new LambdaToolsException($"Failed to parse substitue JSON data: {e.Message}", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                        }
                        jprop.Value = subData;
                        break;

                    default:
                        throw new LambdaToolsException($"Unable to determine how to convert substitute value into the template. " +
                                                       "Make sure to have a default value in the template which is used to determine the type. " +
                                                       "For example \"\" for string fields or {} for JSON objects.",
                                                       LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                    }
                }
                catch (Exception e)
                {
                    throw new LambdaToolsException($"Error setting property {kvp.Key} with value {kvp.Value}: {e.Message}", LambdaToolsException.LambdaErrorCode.ServerlessTemplateSubstitutionError);
                }
            }

            var json = JsonConvert.SerializeObject(root);

            return(json);
        }
Beispiel #23
0
 public LambdaDotNetCLIWrapper(IToolLogger logger, string workingDirectory)
 {
     this._logger           = logger;
     this._workingDirectory = workingDirectory;
 }
 public DeployEnvironmentCommand(IToolLogger logger, string workingDirectory, string[] args)
     : base(logger, workingDirectory, CommandOptions, args)
 {
 }
        public static void BundleFiles(string zipArchivePath, string rootDirectory, string[] files, IToolLogger logger)
        {
            var includedFiles = ConvertToMapOfFiles(rootDirectory, files);

#if NETCORE
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                BundleWithDotNetCompression(zipArchivePath, rootDirectory, includedFiles, logger);
            }
            else
            {
                // Use the native zip utility if it exist which will maintain linux/osx file permissions
                var zipCLI = LambdaDotNetCLIWrapper.FindExecutableInPath("zip");
                if (!string.IsNullOrEmpty(zipCLI))
                {
                    BundleWithZipCLI(zipCLI, zipArchivePath, rootDirectory, includedFiles, logger);
                }
                else
                {
                    throw new LambdaToolsException("Failed to find the \"zip\" utility program in path. This program is required to maintain Linux file permissions in the zip archive.", LambdaToolsException.LambdaErrorCode.FailedToFindZipProgram);
                }
            }
#else
            BundleWithDotNetCompression(zipArchivePath, rootDirectory, includedFiles, logger);
#endif
        }