public LanguageDetectorResult Detect(ISourceRepo sourceRepo) { if (!sourceRepo.FileExists(PythonConstants.RequirementsFileName)) { _logger.LogDebug($"File '{PythonConstants.RequirementsFileName}' does not exist in source repo"); return(null); } string runtimeVersion = DetectPythonVersionFromRuntimeFile(sourceRepo); if (string.IsNullOrEmpty(runtimeVersion)) { var files = sourceRepo.EnumerateFiles( PythonConstants.PythonFileNamePattern, searchSubDirectories: false); if (files == null || !files.Any()) { _logger.LogDebug($"Files with extension '{PythonConstants.PythonFileNamePattern}' do not exist " + "in source repo root"); return(null); } } runtimeVersion = VerifyAndResolveVersion(runtimeVersion); return(new LanguageDetectorResult { Language = PythonConstants.PythonName, LanguageVersion = runtimeVersion, }); }
internal void ReadSettingsFromFile(IDictionary <string, string> settings) { if (!_sourceRepo.FileExists(Constants.BuildEnvironmentFileName)) { _logger.LogDebug( $"Could not find file '{Constants.BuildEnvironmentFileName}' to load environment settings."); return; } var lines = _sourceRepo.ReadAllLines(Constants.BuildEnvironmentFileName); if (lines == null || lines.Length == 0) { return; } foreach (var line in lines) { // Ignore comments and blank lines if (line.StartsWith("#") || string.IsNullOrEmpty(line)) { continue; } // Ignore invalid values if (NameAndValuePairParser.TryParse(line, out var key, out var value)) { settings[key] = value; }
private string DetectPythonVersionFromRuntimeFile(ISourceRepo sourceRepo) { const string versionPrefix = "python-"; // Most Python sites will have at least a .py file in the root, but // some may not. In that case, let them opt in with the runtime.txt // file, which is used to specify the version of Python. if (sourceRepo.FileExists(PythonConstants.RuntimeFileName)) { try { var content = sourceRepo.ReadFile(PythonConstants.RuntimeFileName); var hasPythonVersion = content.StartsWith(versionPrefix, StringComparison.OrdinalIgnoreCase); if (!hasPythonVersion) { _logger.LogDebug("Prefix {verPrefix} was not found in file {rtFileName}", versionPrefix, PythonConstants.RuntimeFileName); return(null); } var pythonVersion = content.Remove(0, versionPrefix.Length); _logger.LogDebug("Found version {pyVer} in runtime file", pythonVersion); return(pythonVersion); } catch (IOException ex) { _logger.LogError(ex, "An error occurred while reading file {rtFileName}", PythonConstants.RuntimeFileName); } } else { _logger.LogDebug("Could not find file '{rtFileName}' in source repo", PythonConstants.RuntimeFileName); } return(null); }
private bool IsPythonApp(ISourceRepo sourceRepo) { if (sourceRepo.FileExists(PythonConstants.RequirementsFileName)) { _logger.LogInformation($"Found {PythonConstants.RequirementsFileName} at the root of the repo."); return(true); } else { _logger.LogInformation( $"Cound not find {PythonConstants.RequirementsFileName} at the root of the repo."); } var files = sourceRepo.EnumerateFiles(PythonConstants.PythonFileNamePattern, searchSubDirectories: true); if (files != null && files.Any()) { _logger.LogInformation( $"Found files with extension '{PythonConstants.PythonFileNamePattern}' " + $"in the repo."); return(true); } else { _logger.LogInformation( $"Could not find any file with extension '{PythonConstants.PythonFileNamePattern}' " + $"in the repo."); } return(false); }
public LanguageDetectorResult Detect(ISourceRepo sourceRepo) { if (!sourceRepo.FileExists(PhpConstants.ComposerFileName)) { _logger.LogDebug($"File '{PhpConstants.ComposerFileName}' does not exist in source repo"); return(null); } dynamic composerFile = null; try { composerFile = sourceRepo.ReadJsonObjectFromFile(PhpConstants.ComposerFileName); } catch (Exception ex) { // We just ignore errors, so we leave malformed composer.json files for Composer to handle, // not us. This prevents us from erroring out when Composer itself might be able to tolerate // some errors in the composer.json file. _logger.LogWarning(ex, $"Exception caught while trying to deserialize {PhpConstants.ComposerFileName}"); } string runtimeVersion = VerifyAndResolveVersion(composerFile?.require?.php?.Value as string); return(new LanguageDetectorResult { Language = PhpConstants.PhpName, LanguageVersion = runtimeVersion, }); }
public string GetSatisfyingSdkVersion( ISourceRepo sourceRepo, string runtimeVersion, IEnumerable <string> availableSdks) { string sdkVersion; if (sourceRepo.FileExists(DotNetCoreConstants.GlobalJsonFileName)) { var globalJsonContent = sourceRepo.ReadFile( Path.Combine(sourceRepo.RootPath, DotNetCoreConstants.GlobalJsonFileName)); _logger.LogDebug( "Detected presence of global.json file with content {globalJsonContent}", globalJsonContent); var globalJsonModel = JsonConvert.DeserializeObject <GlobalJsonModel>(globalJsonContent); sdkVersion = GetSatisfyingSdkVersion(globalJsonModel, availableSdks); _logger.LogDebug( "Resolved sdk version to {resolvedSdkVersion} based on global.json file and available sdk versions", sdkVersion); } else { // As per global.json spec, if a global.json file is not present, then roll forward policy is // considered as 'latestMajor'. This can cause end users apps to fail since in this case even prelreease // versions are considered. So here we minimize the impact by relying on the runtime version instead. // We choose only the 'major' and 'minor' part of the runtime version. // For example, 2.1.14 of runtime will result in a latest minor sdk in '1', for example // 2.1.202 or 2.1.400 var version = new SemVer.Version(runtimeVersion); var globalJsonModel = new GlobalJsonModel { Sdk = new SdkModel { Version = $"{version.Major}.{version.Minor}.100", // Get latest feature and patch of the version RollForward = RollForwardPolicy.LatestFeature, AllowPreRelease = true, }, }; _logger.LogDebug( "global.json file was not find in the repo, so choosing an sdk version which satisfies the " + "version {defaultSdkVersion}, roll forward policy of {defaultRollForwardPolicy} and " + "allowPrerelease value of {defaultAllowPrerelease}.", globalJsonModel.Sdk.Version, globalJsonModel.Sdk.RollForward, globalJsonModel.Sdk.AllowPreRelease); sdkVersion = GetSatisfyingSdkVersion(globalJsonModel, availableSdks); } return(sdkVersion); }
public LanguageDetectorResult Detect(ISourceRepo sourceRepo) { var projectFile = _aspNetCoreWebAppProjectFileProvider.GetRelativePathToProjectFile(sourceRepo); if (string.IsNullOrEmpty(projectFile)) { return(null); } var projectFileDoc = XDocument.Load(new StringReader(sourceRepo.ReadFile(projectFile))); var targetFrameworkElement = projectFileDoc.XPathSelectElement( DotnetCoreConstants.TargetFrameworkElementXPathExpression); var targetFramework = targetFrameworkElement?.Value; if (string.IsNullOrEmpty(targetFramework)) { _logger.LogDebug( $"Could not find 'TargetFramework' element in the project file '{projectFile}'."); return(null); } // If a repo explicitly specifies an sdk version, then just use it as it is. string languageVersion = null; if (sourceRepo.FileExists(DotnetCoreConstants.GlobalJsonFileName)) { var globalJson = GetGlobalJsonObject(sourceRepo); var sdkVersion = globalJson?.sdk?.version?.Value as string; if (sdkVersion != null) { languageVersion = sdkVersion; } } if (string.IsNullOrEmpty(languageVersion)) { languageVersion = DetermineSdkVersion(targetFramework); } if (languageVersion == null) { _logger.LogDebug( $"Could not find a {DotnetCoreConstants.LanguageName} version corresponding to 'TargetFramework'" + $" '{targetFramework}'."); return(null); } languageVersion = VerifyAndResolveVersion(languageVersion); return(new LanguageDetectorResult { Language = DotnetCoreConstants.LanguageName, LanguageVersion = languageVersion }); }
private void TryLogDependencies(string pythonVersion, ISourceRepo repo) { if (!repo.FileExists(PythonConstants.RequirementsFileName)) { return; } try { var deps = repo.ReadAllLines(PythonConstants.RequirementsFileName) .Where(line => !line.TrimStart().StartsWith("#")); _logger.LogDependencies(PythonConstants.PlatformName, pythonVersion, deps); } catch (Exception exc) { _logger.LogWarning(exc, "Exception caught while logging dependencies"); } }
private void TryLogDependencies(string pythonVersion, ISourceRepo repo) { var customRequirementsTxtPath = this.pythonScriptGeneratorOptions.CustomRequirementsTxtPath; var requirementsTxtPath = customRequirementsTxtPath == null ? PythonConstants.RequirementsFileName : customRequirementsTxtPath; if (!repo.FileExists(requirementsTxtPath)) { return; } try { var deps = repo.ReadAllLines(requirementsTxtPath) .Where(line => !line.TrimStart().StartsWith("#")); this.logger.LogDependencies(PythonConstants.PlatformName, pythonVersion, deps); } catch (Exception exc) { this.logger.LogWarning(exc, "Exception caught while logging dependencies"); } }
private bool IsPythonApp(ISourceRepo sourceRepo) { if (sourceRepo.FileExists(PythonConstants.RequirementsFileName)) { _logger.LogInformation($"Found {PythonConstants.RequirementsFileName} at the root of the repo."); return(true); } else { _logger.LogInformation( $"Cound not find {PythonConstants.RequirementsFileName} at the root of the repo."); } var searchSubDirectories = !_options.DisableRecursiveLookUp; if (!searchSubDirectories) { _logger.LogDebug("Skipping search for files in sub-directories as it has been disabled."); } var files = sourceRepo.EnumerateFiles(PythonConstants.PythonFileNamePattern, searchSubDirectories); if (files != null && files.Any()) { _logger.LogInformation( $"Found files with extension '{PythonConstants.PythonFileNamePattern}' " + $"in the repo."); return(true); } else { _logger.LogInformation( $"Could not find any file with extension '{PythonConstants.PythonFileNamePattern}' " + $"in the repo."); } return(false); }
/// <summary> /// Gets the package json object. /// </summary> /// <param name="sourceRepo">The source repository.</param> /// <param name="logger">The logger of Node.js platform.</param> /// <returns>Package json Object.</returns> internal static dynamic GetPackageJsonObject(ISourceRepo sourceRepo, ILogger logger) { if (!sourceRepo.FileExists(NodeConstants.PackageJsonFileName)) { return(null); } dynamic packageJson = null; try { packageJson = sourceRepo.ReadJsonObjectFromFile(NodeConstants.PackageJsonFileName); } catch (Exception exc) { // Leave malformed package.json files for Node.js to handle. // This prevents Oryx from erroring out when Node.js itself might be able to tolerate the file. logger.LogWarning( exc, $"Exception caught while trying to deserialize {NodeConstants.PackageJsonFileName.Hash()}"); } return(packageJson); }
/// <summary> /// Checks whether or not the given repository is a Hugo application. /// </summary> /// <param name="sourceRepo">Source repo for the application.</param> /// <param name="environment">Environment abstraction.</param> /// <returns>True if the app is a Hugo app, false otherwise.</returns> public static bool IsHugoApp(ISourceRepo sourceRepo, IEnvironment environment) { // Check for Hugo environment variables var environmentVariables = environment.GetEnvironmentVariables(); foreach (var key in environmentVariables?.Keys) { if (key.ToString().StartsWith(HugoEnvironmentVariablePrefix)) { return(true); } } // Hugo configuration variables: https://gohugo.io/getting-started/configuration/#all-configuration-settings var tomlFilePaths = new List <string>(); var yamlFilePaths = new List <string>(); var jsonFilePaths = new List <string>(); // Search for config.toml if (sourceRepo.FileExists(NodeConstants.HugoTomlFileName)) { tomlFilePaths.Add(Path.Combine(sourceRepo.RootPath, NodeConstants.HugoTomlFileName)); } // Search for config.yaml if (sourceRepo.FileExists(NodeConstants.HugoYamlFileName)) { yamlFilePaths.Add(Path.Combine(sourceRepo.RootPath, NodeConstants.HugoYamlFileName)); } // Search for config.json if (sourceRepo.FileExists(NodeConstants.HugoJsonFileName)) { jsonFilePaths.Add(Path.Combine(sourceRepo.RootPath, NodeConstants.HugoJsonFileName)); } if (sourceRepo.DirExists(NodeConstants.HugoConfigFolderName)) { var configSourceRepo = new LocalSourceRepo(Path.Combine(sourceRepo.RootPath, NodeConstants.HugoConfigFolderName)); // Search for config/*.toml tomlFilePaths.AddRange(configSourceRepo.EnumerateFiles("*.toml", true)); // Search for config/*.yaml yamlFilePaths.AddRange(configSourceRepo.EnumerateFiles("*.yaml", true)); // Search for config/*.json jsonFilePaths.AddRange(configSourceRepo.EnumerateFiles("*.json", true)); } foreach (var path in tomlFilePaths) { var tomlTable = ParserHelper.ParseTomlFile(sourceRepo, path); if (tomlTable.Keys .Any(k => HugoConfigurationVariables.Contains(k, StringComparer.OrdinalIgnoreCase))) { return(true); } } foreach (var path in yamlFilePaths) { var yamlNode = ParserHelper.ParseYamlFile(sourceRepo, path); if (yamlNode.Children.Keys .Select(k => k.ToString()) .Any(k => HugoConfigurationVariables.Contains(k, StringComparer.OrdinalIgnoreCase))) { return(true); } } foreach (var path in jsonFilePaths) { var jObject = ParserHelper.ParseJsonFile(sourceRepo, path); if (jObject.Children() .Select(c => c.Path) .Any(c => HugoConfigurationVariables.Contains(c, StringComparer.OrdinalIgnoreCase))) { return(true); } } return(false); }
public string GetProjectFile(ISourceRepo sourceRepo) { if (_probedForProjectFile) { return(_projectFile); } var projectEnvVariablePath = _options.Project; string projectFile = null; if (string.IsNullOrEmpty(projectEnvVariablePath)) { // Check if root of the repo has a .csproj file projectFile = sourceRepo .EnumerateFiles($"*.{DotnetCoreConstants.ProjectFileExtensionName}", searchSubDirectories: false) .FirstOrDefault(); } else { projectEnvVariablePath = projectEnvVariablePath.Trim(); projectFile = Path.Combine(sourceRepo.RootPath, projectEnvVariablePath); if (!sourceRepo.FileExists(projectFile)) { _logger.LogWarning($"Could not find the project file '{projectFile}'."); return(null); } } if (projectFile != null) { if (!IsAspNetCoreWebApplicationProject(sourceRepo, projectFile)) { return(null); } return(projectFile); } // Check if any of the sub-directories has a .csproj file and if that .csproj file has references // to typical AspNetCore package references. if (projectFile == null) { var projectFiles = sourceRepo .EnumerateFiles($"*.{DotnetCoreConstants.ProjectFileExtensionName}", searchSubDirectories: true); if (!projectFiles.Any()) { _logger.LogDebug( $"Could not find any files with extension '{DotnetCoreConstants.ProjectFileExtensionName}' in repo."); return(null); } var webAppProjects = new List <string>(); foreach (var file in projectFiles) { if (IsAspNetCoreWebApplicationProject(sourceRepo, file)) { webAppProjects.Add(file); } } if (webAppProjects.Count == 0) { _logger.LogDebug( "Could not find any ASP.NET Core web application projects. " + $"Found the following project files: '{string.Join(" ", projectFiles)}'"); return(null); } if (webAppProjects.Count > 1) { throw new InvalidUsageException( "Ambiguity in selecting an ASP.NET Core web application to build. " + "Found multiple applications: " + string.Join(", ", webAppProjects)); } projectFile = webAppProjects[0]; } // Cache the results _probedForProjectFile = true; _projectFile = projectFile; return(_projectFile); }
private bool IsHugoApp(ISourceRepo sourceRepo, out string appDirectory) { // Hugo configuration variables: // https://gohugo.io/getting-started/configuration/#all-configuration-settings appDirectory = string.Empty; // Search for config.toml if (sourceRepo.FileExists(HugoConstants.TomlFileName) && IsHugoTomlFile(sourceRepo, HugoConstants.TomlFileName)) { return(true); } // Search for config.yml if (sourceRepo.FileExists(HugoConstants.YmlFileName) && IsHugoYamlFile(sourceRepo, HugoConstants.YmlFileName)) { return(true); } // Search for config.yaml if (sourceRepo.FileExists(HugoConstants.YamlFileName) && IsHugoYamlFile(sourceRepo, HugoConstants.YamlFileName)) { return(true); } // Search for config.json if (sourceRepo.FileExists(HugoConstants.JsonFileName) && IsHugoYamlFile(sourceRepo, HugoConstants.JsonFileName)) { return(true); } // NOTE: we do NOT disable looking up into the 'config' folder because it is a special folder // from perspective of Hugo where users can have configuration files. if (sourceRepo.DirExists(HugoConstants.ConfigFolderName)) { // Search for config/**/*.toml var tomlFiles = sourceRepo.EnumerateFiles( "*.toml", searchSubDirectories: true, subDirectoryToSearchUnder: HugoConstants.ConfigFolderName); foreach (var tomlFile in tomlFiles) { if (IsHugoTomlFile(sourceRepo, tomlFile)) { return(true); } } // Search for config/**/*.yaml and config/**/*.yml var yamlFiles = sourceRepo.EnumerateFiles( "*.yaml", searchSubDirectories: true, subDirectoryToSearchUnder: HugoConstants.ConfigFolderName); foreach (var yamlFile in yamlFiles) { if (IsHugoYamlFile(sourceRepo, yamlFile)) { return(true); } } var ymlFiles = sourceRepo.EnumerateFiles( "*.yml", searchSubDirectories: true, subDirectoryToSearchUnder: HugoConstants.ConfigFolderName); foreach (var ymlFile in ymlFiles) { if (IsHugoYamlFile(sourceRepo, ymlFile)) { return(true); } } // Search for config/**/*.json var jsonFiles = sourceRepo.EnumerateFiles( "*.json", searchSubDirectories: true, subDirectoryToSearchUnder: HugoConstants.ConfigFolderName); foreach (var jsonFile in jsonFiles) { if (IsHugoJsonFile(sourceRepo, jsonFile)) { return(true); } } } return(false); }
public string GetRelativePathToProjectFile(ISourceRepo sourceRepo) { if (_probedForProjectFile) { return(_projectFileRelativePath); } var projectEnvVariablePath = _options.Project; string projectFile = null; if (!string.IsNullOrEmpty(projectEnvVariablePath)) { var projectFileWithRelativePath = projectEnvVariablePath.Trim(); projectFile = Path.Combine(sourceRepo.RootPath, projectFileWithRelativePath); if (!sourceRepo.FileExists(projectFile)) { _logger.LogWarning($"Could not find the project file '{projectFile}'."); throw new InvalidUsageException( $"Could not find the project file '{projectFile}' specified by the environment variable" + $" '{EnvironmentSettingsKeys.Project}' with value '{projectFileWithRelativePath}'. " + "Make sure the path to the project file is relative to the root of the repo. " + "For example: PROJECT=src/Dashboard/Dashboard.csproj"); } // NOTE: Do not check if the project file specified by the end user is a web application since this // can be a escape hatch for end users if our logic to determine a web app is incorrect. return(projectFileWithRelativePath); } // Check if root of the repo has a .csproj or a .fsproj file projectFile = GetProjectFileAtRoot(sourceRepo, DotNetCoreConstants.CSharpProjectFileExtension) ?? GetProjectFileAtRoot(sourceRepo, DotNetCoreConstants.FSharpProjectFileExtension); if (projectFile != null) { if (!IsAspNetCoreWebApplicationProject(sourceRepo, projectFile)) { return(null); } return(new FileInfo(projectFile).Name); } // Check if any of the sub-directories has a .csproj file and if that .csproj file has references // to web sdk. if (projectFile == null) { // search for .csproj files var projectFiles = GetAllProjectFilesInRepo( sourceRepo, DotNetCoreConstants.CSharpProjectFileExtension); if (!projectFiles.Any()) { _logger.LogDebug( "Could not find any files with extension " + $"'{DotNetCoreConstants.CSharpProjectFileExtension}' in repo."); // search for .fsproj files projectFiles = GetAllProjectFilesInRepo( sourceRepo, DotNetCoreConstants.FSharpProjectFileExtension); if (!projectFiles.Any()) { _logger.LogDebug( "Could not find any files with extension " + $"'{DotNetCoreConstants.FSharpProjectFileExtension}' in repo."); return(null); } } var webAppProjects = new List <string>(); foreach (var file in projectFiles) { if (IsAspNetCoreWebApplicationProject(sourceRepo, file)) { webAppProjects.Add(file); } } if (webAppProjects.Count == 0) { _logger.LogDebug( "Could not find any ASP.NET Core web application projects. " + $"Found the following project files: '{string.Join(" ", projectFiles)}'"); return(null); } if (webAppProjects.Count > 1) { var projects = string.Join(", ", webAppProjects); throw new InvalidUsageException( "Ambiguity in selecting an ASP.NET Core web application to build. " + $"Found multiple applications: '{projects}'. Use the environment variable " + $"'{EnvironmentSettingsKeys.Project}' to specify the relative path to the project " + "to be deployed."); } projectFile = webAppProjects[0]; } // Cache the results _probedForProjectFile = true; _projectFileRelativePath = GetRelativePathToRoot(projectFile, sourceRepo.RootPath); return(_projectFileRelativePath); }
public LanguageDetectorResult Detect(ISourceRepo sourceRepo) { bool isNodeApp = false; if (sourceRepo.FileExists(NodeConstants.PackageJsonFileName) || sourceRepo.FileExists(NodeConstants.PackageLockJsonFileName) || sourceRepo.FileExists(NodeConstants.YarnLockFileName)) { isNodeApp = true; } else { _logger.LogDebug( $"Could not find {NodeConstants.PackageJsonFileName}/{NodeConstants.PackageLockJsonFileName}" + $"/{NodeConstants.YarnLockFileName} in repo"); } if (!isNodeApp) { // Copying the logic currently running in Kudu: var mightBeNode = false; foreach (var typicalNodeFile in TypicalNodeDetectionFiles) { if (sourceRepo.FileExists(typicalNodeFile)) { mightBeNode = true; break; } } if (mightBeNode) { // Check if any of the known iis start pages exist // If so, then it is not a node.js web site otherwise it is foreach (var iisStartupFile in IisStartupFiles) { if (sourceRepo.FileExists(iisStartupFile)) { _logger.LogDebug( "App in repo is not a Node.js app as it has the file {iisStartupFile}", iisStartupFile); return(null); } } isNodeApp = true; } else { // No point in logging the actual file list, as it's constant _logger.LogDebug("Could not find typical Node.js files in repo"); } } if (isNodeApp) { var packageJson = NodePlatform.GetPackageJsonObject(sourceRepo, _logger); var nodeVersion = DetectNodeVersion(packageJson); return(new LanguageDetectorResult { Language = NodeConstants.NodeJsName, LanguageVersion = nodeVersion, }); } else { _logger.LogDebug("App in repo is not a Node.js app"); } return(null); }