private void ProcessAdditionalFiles(LambdaToolsDefaults defaults, string publishLocation)
        {
            var listOfDependencies = new List <string>();

            var extraDependences = defaults["additional-files"] as string[];

            if (extraDependences != null)
            {
                foreach (var item in extraDependences)
                {
                    listOfDependencies.Add(item);
                }
            }

            foreach (var relativePath in listOfDependencies)
            {
                var    fileName = Path.GetFileName(relativePath);
                string source;
                if (Path.IsPathRooted(relativePath))
                {
                    source = relativePath;
                }
                else
                {
                    source = Path.Combine(publishLocation, relativePath);
                }
                var target = Path.Combine(publishLocation, fileName);
                if (File.Exists(source) && !File.Exists(target))
                {
                    File.Copy(source, target);
                    _logger?.WriteLine($"... publish: Adding additional file {relativePath}");
                }
            }
        }
        public static LambdaToolsDefaults LoadDefaults(string projectLocation)
        {
            string path;

            if (File.Exists(projectLocation))
            {
                path = projectLocation;
            }
            else
            {
                path = Path.Combine(projectLocation, FILE_NAME);
            }

            var defaults = new LambdaToolsDefaults();

            if (!File.Exists(path))
            {
                return(defaults);
            }

            using (var reader = new StreamReader(File.OpenRead(path)))
            {
                try
                {
                    JsonData data = JsonMapper.ToObject(reader) as JsonData;
                    return(new LambdaToolsDefaults(data));
                }
                catch (Exception e)
                {
                    throw new LambdaToolsException($"Error parsing default config {path}: {e.Message}", LambdaToolsException.ErrorCode.DefaultsParseFail, e);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Execute the dotnet publish command and zip up the resulting publish folder.
        /// </summary>
        /// <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);

            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);
                }
            }

            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();
            }

            var zipCLI = DotNetCLIWrapper.FindExecutableInPath("zip");

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

            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="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, LayerPackageInfo layerPackageInfo,
                                                   out string publishLocation, ref string zipArchivePath)
        {
            LogDeprecationMessagesIfNecessary(logger, targetFramework);

            if (string.IsNullOrEmpty(configuration))
            {
                configuration = LambdaConstants.DEFAULT_BUILD_CONFIGURATION;
            }

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

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

            var publishManifestPath = new List <string>();

            if (!string.IsNullOrEmpty(lambdaRuntimePackageStoreManifestContent))
            {
                var tempFile = Path.GetTempFileName();
                File.WriteAllText(tempFile, lambdaRuntimePackageStoreManifestContent);
                publishManifestPath.Add(tempFile);
            }

            if (layerPackageInfo != null)
            {
                foreach (var info in layerPackageInfo.Items)
                {
                    publishManifestPath.Add(info.ManifestPath);
                }
            }

            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, publishManifestPath) != 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);
        }
        /// <summary>
        /// Execute the dotnet store command on the provided package manifest
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="projectLocation"></param>
        /// <param name="outputLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="packageManifest"></param>
        /// <param name="enableOptimization"></param>
        /// <returns></returns>
        public int Store(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string packageManifest, bool enableOptimization)
        {
            if (outputLocation == null)
            {
                throw new ArgumentNullException(nameof(outputLocation));
            }

            if (Directory.Exists(outputLocation))
            {
                try
                {
                    Directory.Delete(outputLocation, true);
                    _logger?.WriteLine("Deleted previous publish folder");
                }
                catch (Exception e)
                {
                    _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}");
                }
            }


            var dotnetCLI = FindExecutableInPath("dotnet.exe");

            if (dotnetCLI == null)
            {
                dotnetCLI = FindExecutableInPath("dotnet");
            }
            if (string.IsNullOrEmpty(dotnetCLI))
            {
                throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH.");
            }

            var fullProjectLocation = this._workingDirectory;

            if (!string.IsNullOrEmpty(projectLocation))
            {
                fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation);
            }

            var fullPackageManifest = Path.Combine(fullProjectLocation, packageManifest);

            _logger?.WriteLine($"... invoking 'dotnet store' for manifest {fullPackageManifest} into output directory {outputLocation}");


            StringBuilder arguments = new StringBuilder("store");

            if (!string.IsNullOrEmpty(outputLocation))
            {
                arguments.Append($" --output \"{outputLocation}\"");
            }

            if (!string.IsNullOrEmpty(targetFramework))
            {
                arguments.Append($" --framework \"{targetFramework}\"");
            }

            arguments.Append($" --manifest \"{fullPackageManifest}\"");
            arguments.Append($" --runtime {LambdaConstants.RUNTIME_HIERARCHY_STARTING_POINT}");

            if (!enableOptimization)
            {
                arguments.Append(" --skip-optimization");
            }

            var psi = new ProcessStartInfo
            {
                FileName               = dotnetCLI,
                Arguments              = arguments.ToString(),
                WorkingDirectory       = this._workingDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

            var handler = (DataReceivedEventHandler)((o, e) =>
            {
                if (string.IsNullOrEmpty(e.Data))
                {
                    return;
                }

                // Skip outputting this warning message as it adds a lot of noise to the output and is not actionable.
                // Full warning message being skipped: message NETSDK1062:
                // Unable to use package assets cache due to I/O error. This can occur when the same project is built
                // more than once in parallel. Performance may be degraded, but the build result will not be impacted.
                if (e.Data.Contains("message NETSDK1062"))
                {
                    return;
                }

                _logger?.WriteLine("... store: " + e.Data);
            });

            int exitCode;

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


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

                proc.EnableRaisingEvents = true;

                proc.WaitForExit();

                exitCode = proc.ExitCode;
            }

            return(exitCode);
        }
        /// <summary>
        /// Executes the dotnet publish command for the provided project
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="projectLocation"></param>
        /// <param name="outputLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="msbuildParameters"></param>
        /// <param name="deploymentTargetPackageStoreManifestContent"></param>
        public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration, string msbuildParameters, IList <string> publishManifests)
        {
            if (outputLocation == null)
            {
                throw new ArgumentNullException(nameof(outputLocation));
            }

            if (Directory.Exists(outputLocation))
            {
                try
                {
                    Directory.Delete(outputLocation, true);
                    _logger?.WriteLine("Deleted previous publish folder");
                }
                catch (Exception e)
                {
                    _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}");
                }
            }

            _logger?.WriteLine($"... invoking 'dotnet publish', working folder '{outputLocation}'");

            var dotnetCLI = FindExecutableInPath("dotnet.exe");

            if (dotnetCLI == null)
            {
                dotnetCLI = FindExecutableInPath("dotnet");
            }
            if (string.IsNullOrEmpty(dotnetCLI))
            {
                throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH.");
            }

            var fullProjectLocation = this._workingDirectory;

            if (!string.IsNullOrEmpty(projectLocation))
            {
                fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation);
            }

            StringBuilder arguments = new StringBuilder("publish");

            if (!string.IsNullOrEmpty(projectLocation))
            {
                arguments.Append($" \"{fullProjectLocation}\"");
            }
            if (!string.IsNullOrEmpty(outputLocation))
            {
                arguments.Append($" --output \"{outputLocation}\"");
            }

            if (!string.IsNullOrEmpty(configuration))
            {
                arguments.Append($" --configuration \"{configuration}\"");
            }

            if (!string.IsNullOrEmpty(targetFramework))
            {
                arguments.Append($" --framework \"{targetFramework}\"");
            }

            if (!string.IsNullOrEmpty(msbuildParameters))
            {
                arguments.Append($" {msbuildParameters}");
            }

            if (!string.Equals("netcoreapp1.0", targetFramework, StringComparison.OrdinalIgnoreCase))
            {
                arguments.Append(" /p:GenerateRuntimeConfigurationFiles=true");

                // If you set the runtime to RUNTIME_HIERARCHY_STARTING_POINT it will trim out the Windows and Mac OS specific dependencies but Razor view precompilation
                // will not run. So only do this packaging optimization if there are no Razor views.
                if (Directory.GetFiles(fullProjectLocation, "*.cshtml", SearchOption.AllDirectories).Length == 0)
                {
                    arguments.Append($" -r {LambdaConstants.RUNTIME_HIERARCHY_STARTING_POINT}");

                    if (msbuildParameters == null ||
                        msbuildParameters.IndexOf("--self-contained", StringComparison.InvariantCultureIgnoreCase) == -1)
                    {
                        arguments.Append(" --self-contained false ");
                    }

                    if (string.IsNullOrEmpty(msbuildParameters) ||
                        !msbuildParameters.Contains("PreserveCompilationContext"))
                    {
                        _logger?.WriteLine("... Disabling compilation context to reduce package size. If compilation context is needed pass in the \"/p:PreserveCompilationContext=false\" switch.");
                        arguments.Append(" /p:PreserveCompilationContext=false");
                    }
                }

                // If we have a manifest of packages already deploy in target deployment environment then write it to disk and add the
                // command line switch
                if (publishManifests != null && publishManifests.Count > 0)
                {
                    foreach (var manifest in publishManifests)
                    {
                        arguments.Append($" --manifest \"{manifest}\"");
                    }
                }
            }

            var psi = new ProcessStartInfo
            {
                FileName               = dotnetCLI,
                Arguments              = arguments.ToString(),
                WorkingDirectory       = this._workingDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

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

            int exitCode;

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


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

                proc.EnableRaisingEvents = true;

                proc.WaitForExit();

                exitCode = proc.ExitCode;
            }

            if (exitCode == 0)
            {
                ProcessAdditionalFiles(defaults, outputLocation);

                var chmodPath = FindExecutableInPath("chmod");
                if (!string.IsNullOrEmpty(chmodPath) && File.Exists(chmodPath))
                {
                    // as we are not invoking through a shell, which would handle
                    // wildcard expansion for us, we need to invoke per-file
                    var files = Directory.GetFiles(outputLocation, "*", SearchOption.TopDirectoryOnly);
                    foreach (var file in files)
                    {
                        var filename = Path.GetFileName(file);
                        var psiChmod = new ProcessStartInfo
                        {
                            FileName               = chmodPath,
                            Arguments              = "+rx \"" + filename + "\"",
                            WorkingDirectory       = outputLocation,
                            RedirectStandardOutput = true,
                            RedirectStandardError  = true,
                            UseShellExecute        = false,
                            CreateNoWindow         = true
                        };

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

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

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

                            if (proc.ExitCode == 0)
                            {
                                this._logger?.WriteLine($"Changed permissions on published file (chmod +rx {filename}).");
                            }
                        }
                    }
                }
            }

            return(exitCode);
        }
예제 #7
0
        /// <summary>
        /// Executes the dotnet publish command for the provided project
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="projectLocation"></param>
        /// <param name="outputLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="msbuildParameters"></param>
        /// <param name="deploymentTargetPackageStoreManifestContent"></param>
        public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration, string msbuildParameters, IList <string> publishManifests)
        {
            if (outputLocation == null)
            {
                throw new ArgumentNullException(nameof(outputLocation));
            }

            if (Directory.Exists(outputLocation))
            {
                try
                {
                    Directory.Delete(outputLocation, true);
                    _logger?.WriteLine("Deleted previous publish folder");
                }
                catch (Exception e)
                {
                    _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}");
                }
            }

            _logger?.WriteLine($"... invoking 'dotnet publish', working folder '{outputLocation}'");

            var dotnetCLI = FindExecutableInPath("dotnet.exe");

            if (dotnetCLI == null)
            {
                dotnetCLI = FindExecutableInPath("dotnet");
            }
            if (string.IsNullOrEmpty(dotnetCLI))
            {
                throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH.");
            }

            var fullProjectLocation = this._workingDirectory;

            if (!string.IsNullOrEmpty(projectLocation))
            {
                fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation);
            }

            StringBuilder arguments = new StringBuilder("publish");

            if (!string.IsNullOrEmpty(projectLocation))
            {
                arguments.Append($" \"{fullProjectLocation}\"");
            }
            if (!string.IsNullOrEmpty(outputLocation))
            {
                arguments.Append($" --output \"{outputLocation}\"");
            }

            if (!string.IsNullOrEmpty(configuration))
            {
                arguments.Append($" --configuration \"{configuration}\"");
            }

            if (!string.IsNullOrEmpty(targetFramework))
            {
                arguments.Append($" --framework \"{targetFramework}\"");
            }

            if (!string.IsNullOrEmpty(msbuildParameters))
            {
                arguments.Append($" {msbuildParameters}");
            }

            if (!string.Equals("netcoreapp1.0", targetFramework, StringComparison.OrdinalIgnoreCase))
            {
                arguments.Append(" /p:GenerateRuntimeConfigurationFiles=true");

                // Define an action to set the runtime and self-contained switches.
                var applyRuntimeSwitchAction = (Action)(() =>
                {
                    if (msbuildParameters == null ||
                        msbuildParameters.IndexOf("--runtime", StringComparison.InvariantCultureIgnoreCase) == -1)
                    {
                        arguments.Append($" --runtime {LambdaUtilities.DetermineRuntimeParameter(targetFramework)}");
                    }

                    if (msbuildParameters == null ||
                        msbuildParameters.IndexOf("--self-contained", StringComparison.InvariantCultureIgnoreCase) == -1)
                    {
                        arguments.Append(" --self-contained false ");
                    }
                });

                // This is here to not change existing behavior for the 2.0 and 2.1 runtimes. For those runtimes if
                // cshtml files are being used we need to support that cshtml being compiled at runtime. In order to do that we
                // need to not turn PreserveCompilationContext which provides reference assemblies to the runtime
                // compilation and not set a runtime.
                //
                // If there are no cshtml then disable PreserveCompilationContext to reduce package size and continue
                // to use the same runtime identifier that we used when those runtimes were launched.
                if (new string[] { "netcoreapp2.0", "netcoreapp2.1" }.Contains(targetFramework))
                {
                    if (Directory.GetFiles(fullProjectLocation, "*.cshtml", SearchOption.AllDirectories).Length == 0)
                    {
                        applyRuntimeSwitchAction();

                        if (string.IsNullOrEmpty(msbuildParameters) ||
                            !msbuildParameters.Contains("PreserveCompilationContext"))
                        {
                            _logger?.WriteLine("... Disabling compilation context to reduce package size. If compilation context is needed pass in the \"/p:PreserveCompilationContext=false\" switch.");
                            arguments.Append(" /p:PreserveCompilationContext=false");
                        }
                    }
                }
                else
                {
                    applyRuntimeSwitchAction();
                }

                // If we have a manifest of packages already deploy in target deployment environment then write it to disk and add the
                // command line switch
                if (publishManifests != null && publishManifests.Count > 0)
                {
                    foreach (var manifest in publishManifests)
                    {
                        arguments.Append($" --manifest \"{manifest}\"");
                    }
                }
            }

            // echo the full dotnet command for debug
            _logger?.WriteLine($"... dotnet {arguments}");

            var psi = new ProcessStartInfo
            {
                FileName               = dotnetCLI,
                Arguments              = arguments.ToString(),
                WorkingDirectory       = this._workingDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

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

            int exitCode;

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


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

                proc.EnableRaisingEvents = true;

                proc.WaitForExit();

                exitCode = proc.ExitCode;
            }

            if (exitCode == 0)
            {
                ProcessAdditionalFiles(defaults, outputLocation);

                var chmodPath = FindExecutableInPath("chmod");
                var touchPath = FindExecutableInPath("touch");
                if (!string.IsNullOrEmpty(chmodPath) && File.Exists(chmodPath))
                {
                    // as we are not invoking through a shell, which would handle
                    // wildcard expansion for us, we need to invoke per-file
                    var files = Directory.GetFiles(outputLocation, "*", SearchOption.TopDirectoryOnly);
                    foreach (var file in files)
                    {
                        var filename = Path.GetFileName(file);
                        ApplyCommand(outputLocation, chmodPath, $"+rx \"{filename}\"", handler);
                        ApplyCommand(outputLocation, touchPath, $"-a -m -t 201512180130.09 \"{filename}\"", handler);
                    }
                }
            }

            return(exitCode);
        }
예제 #8
0
        /// <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();
            }


            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);
                }
            }


            return(true);
        }
예제 #9
0
        /// <summary>
        /// Generates deployment manifest for staged content
        /// </summary>
        /// <param name="defaults"></param>
        /// <param name="projectLocation"></param>
        /// <param name="outputLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="configuration"></param>
        /// <param name="deploymentTargetPackageStoreManifestContent"></param>
        public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration, string deploymentTargetPackageStoreManifestContent)
        {
            if (Directory.Exists(outputLocation))
            {
                try
                {
                    Directory.Delete(outputLocation, true);
                    _logger?.WriteLine("Deleted previous publish folder");
                }
                catch (Exception e)
                {
                    _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}");
                }
            }

            _logger?.WriteLine($"... invoking 'dotnet publish', working folder '{outputLocation}'");

            var dotnetCLI = FindExecutableInPath("dotnet.exe");

            if (dotnetCLI == null)
            {
                dotnetCLI = FindExecutableInPath("dotnet");
            }
            if (string.IsNullOrEmpty(dotnetCLI))
            {
                throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH.");
            }

            var fullProjectLocation = this._workingDirectory;

            if (!string.IsNullOrEmpty(projectLocation))
            {
                fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation);
            }

            StringBuilder arguments = new StringBuilder("publish");

            if (!string.IsNullOrEmpty(projectLocation))
            {
                arguments.Append($" \"{fullProjectLocation}\"");
            }
            if (!string.IsNullOrEmpty(outputLocation))
            {
                arguments.Append($" --output \"{outputLocation}\"");
            }

            if (!string.IsNullOrEmpty(configuration))
            {
                arguments.Append($" --configuration \"{configuration}\"");
            }

            if (!string.IsNullOrEmpty(targetFramework))
            {
                arguments.Append($" --framework \"{targetFramework}\"");
            }

            string manifestPath = null;

            if (!string.Equals("netcoreapp1.0", targetFramework, StringComparison.OrdinalIgnoreCase))
            {
                arguments.Append(" /p:GenerateRuntimeConfigurationFiles=true");

                // If you set the runtime linux-x64 it will trim out the Windows and Mac OS specific dependencies but Razor view precompilation
                // will not run. So only do this packaging optimization if there are no Razor views.
                if (Directory.GetFiles(fullProjectLocation, "*.cshtml", SearchOption.AllDirectories).Length == 0)
                {
                    arguments.Append(" -r linux-x64 --self-contained false /p:PreserveCompilationContext=false");
                }

                // If we have a manifest of packages already deploy in target deployment environment then write it to disk and add the
                // command line switch
                if (!string.IsNullOrEmpty(deploymentTargetPackageStoreManifestContent))
                {
                    manifestPath = Path.GetTempFileName();
                    File.WriteAllText(manifestPath, deploymentTargetPackageStoreManifestContent);

                    arguments.Append($" --manifest \"{manifestPath}\"");
                }
            }

            var psi = new ProcessStartInfo
            {
                FileName               = dotnetCLI,
                Arguments              = arguments.ToString(),
                WorkingDirectory       = this._workingDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

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

            int exitCode;

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


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

                proc.EnableRaisingEvents = true;

                proc.WaitForExit();

                exitCode = proc.ExitCode;
            }

            // If we wrote a temporary manifest file then clean it up after dotnet publish has executed.
            if (manifestPath != null && File.Exists(manifestPath))
            {
                File.Delete(manifestPath);
            }

            if (exitCode == 0)
            {
                ProcessAdditionalFiles(defaults, Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation), outputLocation);

                var chmodPath = FindExecutableInPath("chmod");
                if (!string.IsNullOrEmpty(chmodPath) && File.Exists(chmodPath))
                {
                    // as we are not invoking through a shell, which would handle
                    // wildcard expansion for us, we need to invoke per-file
                    var dllFiles = Directory.GetFiles(outputLocation, "*.dll", SearchOption.TopDirectoryOnly);
                    foreach (var dllFile in dllFiles)
                    {
                        var dllFilename = Path.GetFileName(dllFile);
                        var psiChmod    = new ProcessStartInfo
                        {
                            FileName               = chmodPath,
                            Arguments              = "+r " + dllFilename,
                            WorkingDirectory       = outputLocation,
                            RedirectStandardOutput = true,
                            RedirectStandardError  = true,
                            UseShellExecute        = false,
                            CreateNoWindow         = true
                        };

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

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

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

                            if (proc.ExitCode == 0)
                            {
                                this._logger?.WriteLine($"Changed permissions on published dll (chmod +r {dllFilename}).");
                            }
                        }
                    }
                }
            }

            return(exitCode);
        }
예제 #10
0
        /// <summary>
        /// Generates deployment manifest for staged content
        /// </summary>
        /// <param name="outputLocation"></param>
        /// <param name="targetFramework"></param>
        /// <param name="iisAppPath"></param>
        /// <param name="configuration"></param>
        /// <param name="logger"></param>
        public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration)
        {
            if (Directory.Exists(outputLocation))
            {
                try
                {
                    Directory.Delete(outputLocation, true);
                    _logger?.WriteLine("Deleted previous publish folder");
                }
                catch (Exception e)
                {
                    _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}");
                }
            }

            _logger?.WriteLine($"... invoking 'dotnet publish', working folder '{outputLocation}'");

            var dotnetCLI = FindExecutableInPath("dotnet.exe");

            if (dotnetCLI == null)
            {
                dotnetCLI = FindExecutableInPath("dotnet");
            }
            if (string.IsNullOrEmpty(dotnetCLI))
            {
                throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH.");
            }

            StringBuilder arguments = new StringBuilder("publish");

            if (!string.IsNullOrEmpty(projectLocation))
            {
                arguments.Append($" \"{Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation)}\"");
            }
            if (!string.IsNullOrEmpty(outputLocation))
            {
                arguments.Append($" --output \"{outputLocation}\"");
            }

            if (!string.IsNullOrEmpty(configuration))
            {
                arguments.Append($" --configuration \"{configuration}\"");
            }

            if (!string.IsNullOrEmpty(targetFramework))
            {
                arguments.Append($" --framework \"{targetFramework}\"");
            }


            var psi = new ProcessStartInfo
            {
                FileName               = dotnetCLI,
                Arguments              = arguments.ToString(),
                WorkingDirectory       = this._workingDirectory,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };

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

            int exitCode;

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


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

                proc.EnableRaisingEvents = true;

                proc.WaitForExit();

                exitCode = proc.ExitCode;
            }

            if (exitCode == 0)
            {
                ProcessAdditionalFiles(defaults, Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation), outputLocation);

                var chmodPath = FindExecutableInPath("chmod");
                if (!string.IsNullOrEmpty(chmodPath) && File.Exists(chmodPath))
                {
                    // as we are not invoking through a shell, which would handle
                    // wildcard expansion for us, we need to invoke per-file
                    var dllFiles = Directory.GetFiles(outputLocation, "*.dll", SearchOption.TopDirectoryOnly);
                    foreach (var dllFile in dllFiles)
                    {
                        var dllFilename = Path.GetFileName(dllFile);
                        var psiChmod    = new ProcessStartInfo
                        {
                            FileName               = chmodPath,
                            Arguments              = "+r " + dllFilename,
                            WorkingDirectory       = outputLocation,
                            RedirectStandardOutput = true,
                            RedirectStandardError  = true,
                            UseShellExecute        = false,
                            CreateNoWindow         = true
                        };

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

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

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

                            if (proc.ExitCode == 0)
                            {
                                this._logger?.WriteLine($"Changed permissions on published dll (chmod +r {dllFilename}).");
                            }
                        }
                    }
                }
            }

            return(exitCode);
        }
        /// <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 = FlattenRuntimeFolder(logger, publishLocation);

            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);
        }