/// <summary> /// Whether the driver support the specified uri. /// </summary> /// <param name="io">The input/output instance.</param> /// <param name="config">The config instance.</param> /// <param name="uri">Thr specified uri.</param> /// <param name="deep">Whether is deep checker.</param> /// <returns>True if the driver is supported.</returns> public static bool IsSupport(IIO io, Config config, string uri, bool deep = false) { if (Regex.IsMatch(uri, @"(^git://|\.git/?$|git(?:olite)?@|//git\.|//github\.com/|//gitlib\.com/)", RegexOptions.IgnoreCase)) { return(true); } bool ProcessCheck(string command, string cwd) { var process = new BucketProcessExecutor(io); return(process.Execute(command, cwd) == 0); } if (FileSystemLocal.IsLocalPath(uri)) { uri = FileSystemLocal.GetPlatformPath(uri); if (!Directory.Exists(uri)) { return(false); } return(ProcessCheck("git tag", uri)); } if (!deep) { return(false); } return(ProcessCheck($"git ls-remote --heads {ProcessExecutor.Escape(uri)}", null)); }
/// <inheritdoc /> public virtual void Copy(string uri, string target, IProgress <ProgressChanged> progress = null, IReadOnlyDictionary <string, object> additionalOptions = null) { FileSystemLocal.EnsureDirectory(Directory.GetParent(target).FullName); using (var saved = new FileStream(target, FileMode.CreateNew, FileAccess.Write, FileShare.None, buffer.Length)) { GetRequest(uri, saved, progress, additionalOptions); } }
/// <summary> /// Copy a local file into the cache. /// </summary> /// <param name="cache">The cache system instance.</param> /// <param name="file">The cache file name.</param> /// <param name="source">The source file absolute path.</param> /// <returns>True if the copy successful.</returns> public static bool CopyFrom(this ICache cache, string file, string source) { if (!cache.Enable) { return(false); } var local = new FileSystemLocal(); using (var stream = local.Read(source)) { cache.Write(file, stream); } return(true); }
/// <summary> /// Copy a file out of the cache to local disk. /// </summary> /// <param name="cache">The cache system instance.</param> /// <param name="file">The cache file name.</param> /// <param name="target">Absolute path to the local disk.</param> /// <returns>True if the copy successful.</returns> public static bool CopyTo(this ICache cache, string file, string target) { if (!cache.Enable) { return(false); } var local = new FileSystemLocal(); using (var stream = cache.Read(file)) { local.Write(target, stream); } return(true); }
public void Initialize() { root = Helper.GetTestFolder <TestsFileSystemLocal>(); fileSystem = new FileSystemLocal(root); if (Directory.Exists(root)) { Directory.Delete(root, true); } if (File.Exists(root)) { File.Delete(root); } Directory.CreateDirectory(root); }
/// <inheritdoc /> public override void Initialize() { string cacheUri; if (FileSystemLocal.IsLocalPath(Uri)) { Uri = Regex.Replace(Uri, @"[\\/]\.git/?$", string.Empty); repositoryDirectory = Uri; cacheUri = Path.GetFullPath(Uri); } else { var cacheVcsDir = Config.Get(Settings.CacheVcsDir); if (!CacheFileSystem.IsUsable(cacheVcsDir)) { throw new RuntimeException("DriverGit requires a usable cache directory, and it looks like you set it to be disabled."); } if (Regex.IsMatch("^ssh://[^@]+@[^:]+:[^0-9]+", Uri)) { throw new InvalidArgumentException($"The source URL {Uri} is invalid, ssh URLs should have a port number after \":\".{Environment.NewLine}Use ssh://[email protected]:22/path or just [email protected]:path if you do not want to provide a password or custom port."); } Git.CleanEnvironment(); var fileSystem = new FileSystemLocal($"{cacheVcsDir}/{CacheFileSystem.FormatCacheFolder(Uri)}/"); var git = new Git(IO, Config, Process, fileSystem); repositoryDirectory = fileSystem.Root; if (!git.SyncMirror(Uri, repositoryDirectory)) { IO.WriteError($"<error>Failed to update {Uri}, package information from this repository may be outdated</error>"); } cacheUri = Uri; } GetTags(); GetBranches(); cache = new CacheFileSystem($"{Config.Get(Settings.CacheRepoDir)}/{CacheFileSystem.FormatCacheFolder(cacheUri)}", IO); }
/// <summary> /// extract <paramref name="file"/> to <paramref name="extractPath"/> with unzip command. /// </summary> protected internal virtual void ExtractWithUnzipCommand(string file, string extractPath, bool isFallback = false) { // When called after a ZipArchive failed, perhaps // there is some files to overwrite var overwrite = isFallback ? "-o " : string.Empty; var command = $"unzip -qq {overwrite}{ProcessExecutor.Escape(file)} -d {ProcessExecutor.Escape(extractPath)}"; FileSystemLocal.EnsureDirectory(extractPath); SException processException; try { if (process.Execute(command, out _, out string stderr) == 0) { return; } throw new RuntimeException( $"Failed to execute {command} {Environment.NewLine}{Environment.NewLine} {stderr}"); } #pragma warning disable CA1031 catch (SException ex) #pragma warning restore CA1031 { processException = ex; } if (isFallback) { ExceptionDispatchInfo.Capture(processException).Throw(); } io.WriteError($" {processException.Message}"); io.WriteError($" The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)"); io.WriteError($" Unzip with unzip command failed, falling back to {nameof(ZipArchive)}."); ExtractWithZipArchive(file, extractPath, true); }
private void ProcessAction(string[] sourceUris, Action <string> closure) { var uris = new Queue <string>(sourceUris); Guard.Requires <UnexpectedException>(uris.Count > 0, "The number of valid download addresses must be greater than 0."); while (uris.Count > 0) { var uri = uris.Dequeue(); try { if (FileSystemLocal.IsLocalPath(uri)) { uri = ProcessLocalSourceUri(uri); } closure(uri); break; } catch (SException ex) { if (IO.IsDebug) { IO.WriteError($"Failed: [{ex.GetType()}] {ex.Message}"); } else if (uris.Count > 0) { IO.WriteError(" Failed, trying the next URI"); } if (uris.Count <= 0) { throw; } } } }
/// <summary> /// Transfer the permissions of the source file(or dir) to the target file(or dir). /// </summary> /// <remarks>It is not allowed to pass permissions between folders and files, this will return false.</remarks> /// <returns>True if transfer successful. false if the <paramref name="destination"/> not exists.</returns> public static bool TransferPerms(string source, string destination) { if (!FileSystem.Exists(source) || !FileSystem.Exists(destination)) { return(false); } var sourceIsDir = FileSystemLocal.IsDirectory(source); var destinationIsDir = FileSystemLocal.IsDirectory(destination); // Ensure source is same type for the destination. if (sourceIsDir != destinationIsDir) { return(false); } if (Platform.IsWindows) { return(PermissionWindows.TransferPerms(source, destination, sourceIsDir)); } return(PermissionUnix.TransferPerms(source, destination)); }
/// <inheritdoc /> public override void Install(IPackage package, string cwd, bool output) { if (output) { io.WriteError($" - Installing <info>{package.GetName()}</info> (<comment>{package.GetVersionPrettyFull()}</comment>): Extracting archive"); } else { io.WriteError("Extracting archive", false); } fileSystem.Delete(cwd); var temporaryDir = Path.Combine( GetTempDirectory(), "extract", Security.Md5(Guid.NewGuid().ToString()).Substring(0, 7)); var downloadedFilePath = GetDownloadedFilePath(package, cwd); try { FileSystemLocal.EnsureDirectory(temporaryDir); try { Extract(package, downloadedFilePath, temporaryDir); } catch { // remove cache if the file was corrupted. ClearLastCacheWrite(package); throw; } // Expand a single top-level directory for a // better experience. string ExtractSingleDirAtTopLevel(string path) { var contents = fileSystem.GetContents(path); var files = contents.GetFiles(); var dirs = contents.GetDirectories(); files = Arr.Filter(files, (file) => !file.EndsWith(".DS_Store", StringComparison.Ordinal)); if ((dirs.Length + files.Length) != 1 || files.Length == 1) { return(path); } return(ExtractSingleDirAtTopLevel(dirs[0])); } fileSystem.Move(ExtractSingleDirAtTopLevel(temporaryDir), cwd); } catch { fileSystem.Delete(cwd); throw; } finally { fileSystem.Delete(temporaryDir); fileSystem.Delete(downloadedFilePath); } }
/// <summary> /// Create a new <see cref="DownloadManager"/> instance. /// </summary> /// <param name="io">The input/output instance.</param> /// <param name="config">The config instance.</param> /// <param name="transport">The transport instance.</param> /// <param name="eventDispatcher">The event dispatcher instance.</param> public static DownloadManager CreateManager(IIO io, Config config, ITransport transport = null, IEventDispatcher eventDispatcher = null) { var manager = new DownloadManager(io); InstallationSource GetInstallationSource(string prefer) { switch (prefer ?? "auto") { case "dist": return(InstallationSource.Dist); case "source": return(InstallationSource.Source); case "auto": default: return(InstallationSource.Auto); } } try { var prefer = GetInstallationSource(config.Get(Settings.PreferredInstall)); switch (prefer) { case InstallationSource.Dist: manager.SetPreferDist(); break; case InstallationSource.Source: manager.SetPreferSource(); break; default: break; } } catch (ConfigException) { // Maybe the developer is using fine configuration. var preferred = config.Get <ConfigPreferred>(Settings.PreferredInstall); manager.SetPreferences(Arr.Map(preferred, (item) => (item.Key, GetInstallationSource(item.Value)))); } transport = transport ?? new TransportHttp(io, config); var process = new BucketProcessExecutor(io); var fileSystem = new FileSystemLocal(process: process); ICache cache = null; if (config.Get(Settings.CacheFilesTTL) > 0) { cache = new CacheFileSystem(config.Get(Settings.CacheFilesDir), io, "a-z0-9_./", fileSystem); } manager.SetDownloader("git", new DownloaderGit(io, config, process, fileSystem)); manager.SetDownloader("zip", new DownloaderZip(io, config, transport, eventDispatcher, cache, fileSystem, process)); manager.SetDownloader("file", new DownloaderFile(io, config, transport, eventDispatcher, cache, fileSystem)); return(manager); }
public void TestAltDirectorySeparatorChar() { var foo = new FileSystemLocal("c:/folder/foo"); Assert.AreNotEqual(null, foo); }
public void TestIsLocalPath(bool expected, string path) { Assert.AreEqual(expected, FileSystemLocal.IsLocalPath(path)); }
/// <summary> /// Creates a <see cref="Bucket"/> object from specified <paramref name="cwd"/>. /// </summary> /// <remarks>If no <paramref name="cwd"/> is given, the current environment path will be used.</remarks> /// <param name="io">The input/output instance.</param> /// <param name="localBucket">either a configuration filename to read from, if null it will read from the default filename.</param> /// <param name="disablePlugins">Whether plugins should not be loaded.</param> /// <param name="cwd">Current working directory.</param> /// <param name="fullLoad">Whether to initialize everything or only main project stuff (used when loading the global bucket and auth).</param> /// <exception cref="InvalidArgumentException">If local config file not exists.</exception> public Bucket CreateBucket(IIO io, object localBucket = null, bool disablePlugins = false, string cwd = null, bool fullLoad = true) { cwd = cwd ?? Environment.CurrentDirectory; var localFileSystem = new FileSystemLocal(cwd); var config = CreateConfig(io, cwd); string bucketFile = null; if (localBucket == null || localBucket is string) { bucketFile = localBucket?.ToString(); bucketFile = string.IsNullOrEmpty(bucketFile) ? GetBucketFile() : bucketFile; var localBucketFile = new JsonFile(bucketFile, localFileSystem, io); if (!localBucketFile.Exists()) { string message; if (bucketFile == "./bucket.json" || bucketFile == "bucket.json") { message = $"Bucket could not find a bucket.json file in {cwd}"; } else { message = $"Bucket could not find the config file {bucketFile} in {cwd}"; } var instructions = "To initialize a project, please create a bucket.json"; throw new InvalidArgumentException(message + Environment.NewLine + instructions); } localBucketFile.Validate(false); io.WriteError($"Loading bucket file: {localBucketFile.GetPath()}", true, Verbosities.Debug); config.Merge(localBucketFile.Read()); config.SetSourceBucket(new JsonConfigSource(localBucketFile)); var localAuthFile = new JsonFile("./auth.json", localFileSystem, io); if (localAuthFile.Exists()) { io.WriteError($"Loading bucket file: {localAuthFile.GetPath()}", true, Verbosities.Debug); config.Merge(new JObject { ["config"] = localAuthFile.Read(), }); config.SetSourceAuth(new JsonConfigSource(localAuthFile, true)); } localBucket = localBucketFile.Read <ConfigBucket>(); } else if (localBucket is ConfigBucket configBucket) { config.Merge(JObject.FromObject(configBucket)); } else { throw new UnexpectedException($"Params \"{localBucket}\" only allow {nameof(ConfigBucket)} type or string path."); } if (fullLoad) { new LoaderIOConfiguration(io).Load(config); } // initialize bucket. var bucket = new Bucket(); bucket.SetConfig(config); var transport = CreateTransport(io, config); // initialize event dispatcher. var dispatcher = new BEventDispatcher(bucket, io); bucket.SetEventDispatcher(dispatcher); // initialize repository manager var repositoryManager = RepositoryFactory.CreateManager(io, config, dispatcher); bucket.SetRepositoryManager(repositoryManager); InitializeLocalInstalledRepository(io, repositoryManager, config.Get(Settings.VendorDir)); // load root package var versionParser = new BVersionParser(); var loader = new LoaderPackageRoot(repositoryManager, config, io, versionParser); var package = loader.Load <IPackageRoot>((ConfigBucket)localBucket); bucket.SetPackage(package); // create installation manager var installationManager = CreateInstallationManager(); bucket.SetInstallationManager(installationManager); if (fullLoad) { var downloadManager = DownloadFactory.CreateManager(io, config, transport, dispatcher); bucket.SetDownloadManager(downloadManager); // todo: initialize archive manager } // must happen after downloadManager is created since they read it out of bucket. InitializeDefaultInstallers(installationManager, bucket, io); if (fullLoad) { Bucket globalBucket = null; if (BaseFileSystem.GetNormalizePath(cwd) != BaseFileSystem.GetNormalizePath(config.Get(Settings.Home))) { globalBucket = CreateGlobalBucket(io, config, disablePlugins, false); } var pluginManager = new PluginManager(io, bucket, globalBucket, disablePlugins); bucket.SetPluginManager(pluginManager); pluginManager.LoadInstalledPlugins(); } // init locker if locker exists. if (fullLoad && !string.IsNullOrEmpty(bucketFile)) { var lockFile = Path.GetExtension(bucketFile) == ".json" ? bucketFile.Substring(0, bucketFile.Length - 5) + ".lock" : bucketFile + ".lock"; var locker = new Locker( io, new JsonFile(lockFile, localFileSystem, io), installationManager, localFileSystem.Read(bucketFile).ToText()); bucket.SetLocker(locker); } if (fullLoad) { // Raises an event only when it is fully loaded. dispatcher.Dispatch(PluginEvents.Init, this); } if (fullLoad) { // once everything is initialized we can purge packages from // local repos if they have been deleted on the filesystem. PurgePackages(repositoryManager.GetLocalInstalledRepository(), installationManager); } return(bucket); }
/// <inheritdoc /> protected override int Execute(IInput input, IOutput output) { var file = input.GetArgument("file") ?? Factory.GetBucketFile(); var io = GetIO(); var fileSystem = new FileSystemLocal(); if (!fileSystem.Exists(file)) { io.WriteError($"<error>{file} not found.</error>"); return(ExitCodes.FileNotFoundException); } var checkPublish = !input.GetOption("no-check-publish"); var checkLock = !input.GetOption("no-check-lock"); var isStrict = input.GetOption("strict"); var validator = new ValidatorBucket(fileSystem, io); var(warnings, publishErrors, errors) = validator.Validate(file); var lockErrors = Array.Empty <string>(); var bucket = Factory.Create(io, file, input.HasRawOption("--no-plugins")); var locker = bucket.GetLocker(); if (locker.IsLocked() && !locker.IsFresh()) { lockErrors = new[] { "The lock file is not up to date with the latest changes in bucket.json, it is recommended that you run `bucket update`." }; } OuputResult(file, ref errors, ref warnings, checkPublish, publishErrors, checkLock, lockErrors, isStrict); int GetStrictExitCode() { return((isStrict && !warnings.Empty()) ? ExitCodes.ValidationWarning : GameBox.Console.ExitCodes.Normal); } var exitCode = errors.Length > 0 ? ExitCodes.ValidationErrors : GetStrictExitCode(); if (input.GetOption("with-dependencies")) { var localInstalledRepository = bucket.GetRepositoryManager().GetLocalInstalledRepository(); foreach (var package in localInstalledRepository.GetPackages()) { var path = bucket.GetInstallationManager().GetInstalledPath(package); file = Path.Combine(path, "bucket.json"); if (!Directory.Exists(path) || !File.Exists(file)) { continue; } (warnings, publishErrors, errors) = validator.Validate(file); OuputResult(file, ref errors, ref warnings, checkPublish, publishErrors, isStrict: isStrict); var depCode = !errors.Empty() ? ExitCodes.ValidationErrors : GetStrictExitCode(); exitCode = Math.Max(depCode, exitCode); } } var commandEvent = new CommandEventArgs(PluginEvents.Command, "validate", input, output); bucket.GetEventDispatcher().Dispatch(this, commandEvent); return(Math.Max(exitCode, commandEvent.ExitCode)); }
/// <summary> /// Initializes a new instance of the <see cref="ArchiverZip"/> class. /// </summary> public ArchiverZip() { fileSystem = new FileSystemLocal(); }