예제 #1
0
        private static async Task <Stream> InternalPreparePythonDeployment(IEnumerable <string> files, string functionAppRoot)
        {
            var packagesPath = await RestorePythonRequirements(functionAppRoot);

            return(ZipHelper.CreateZip(files.Concat(FileSystemHelpers.GetFiles(packagesPath)), functionAppRoot));
        }
        internal static async Task <Stream> GetPythonDeploymentPackage(IEnumerable <string> files, string functionAppRoot, bool buildNativeDeps, BuildOption buildOption, string additionalPackages)
        {
            var reqTxtFile = Path.Combine(functionAppRoot, Constants.RequirementsTxt);

            if (!FileSystemHelpers.FileExists(reqTxtFile))
            {
                throw new CliException($"{Constants.RequirementsTxt} is not found. " +
                                       $"{Constants.RequirementsTxt} is required for python function apps. Please make sure to generate one before publishing.");
            }
            var packagesLocation = Path.Combine(functionAppRoot, Constants.ExternalPythonPackages);

            if (FileSystemHelpers.DirectoryExists(packagesLocation))
            {
                // Only update packages if checksum of requirements.txt does not match
                // If build option is remote, we don't need to verify if packages are in sync, as we need to delete them regardless
                if (buildOption != BuildOption.Remote && await ArePackagesInSync(reqTxtFile, packagesLocation))
                {
                    ColoredConsole.WriteLine(Yellow($"Directory {Constants.ExternalPythonPackages} already in sync with {Constants.RequirementsTxt}. Skipping restoring dependencies..."));
                    return(ZipHelper.CreateZip(files.Union(FileSystemHelpers.GetFiles(packagesLocation)), functionAppRoot));
                }
                ColoredConsole.WriteLine($"Deleting the old {Constants.ExternalPythonPackages} directory");
                FileSystemHelpers.DeleteDirectorySafe(packagesLocation);
            }

            FileSystemHelpers.EnsureDirectory(packagesLocation);

            // Only one of the remote build or build-native-deps flag can be chosen
            if (buildNativeDeps && buildOption == BuildOption.Remote)
            {
                throw new CliException("Cannot perform '--build-native-deps' along with '--build remote'");
            }

            if (buildNativeDeps)
            {
                if (CommandChecker.CommandExists("docker") && await DockerHelpers.VerifyDockerAccess())
                {
                    await RestorePythonRequirementsDocker(functionAppRoot, packagesLocation, additionalPackages);
                }
                else
                {
                    throw new CliException("Docker is required to build native dependencies for python function apps");
                }
            }
            else if (buildOption == BuildOption.Remote)
            {
                // No-ops, python packages will be resolved on the server side
            }
            else
            {
                await RestorePythonRequirementsPackapp(functionAppRoot, packagesLocation);
            }

            // No need to generate and compare .md5 when using remote build
            if (buildOption != BuildOption.Remote)
            {
                // Store a checksum of requirements.txt
                var md5FilePath = Path.Combine(packagesLocation, $"{Constants.RequirementsTxt}.md5");
                await FileSystemHelpers.WriteAllTextToFileAsync(md5FilePath, SecurityHelpers.CalculateMd5(reqTxtFile));
            }

            return(ZipHelper.CreateZip(files.Union(FileSystemHelpers.GetFiles(packagesLocation)), functionAppRoot));
        }