/// <summary> /// Writes a ZIP file to a stream, including the selected files from the static directory. /// </summary> /// <param name="directory">The static directory instance.</param> /// <param name="zipStream">The output stream.</param> /// <param name="searchPattern"> /// Optionally specifies a file name pattern using standard file system wildcards /// like <b>[*]</b> and <b>[?]</b>. This defaults to including all files. /// </param> /// <param name="searchOptions">Optionally perform a recursive search. This defaults to /// <see cref="SearchOption.TopDirectoryOnly"/>. /// </param> /// <param name="zipOptions"> /// Additional options that control things like whether the files are zipped within /// the parent directory or whether the files are assumed to contain UTF-8 text and /// that Windows style CRLF line endings are to be converted to Linux compatible LF /// endings. You can combine options by bitwise ORing them. This defaults to /// <see cref="StaticZipOptions.None"/>. /// </param> /// <remarks> /// <note> /// The current implementation loads the files into memory so this isn't really suitable /// for zipping very large files. /// </note> /// </remarks> public static void Zip( this IStaticDirectory directory, Stream zipStream, string searchPattern = null, SearchOption searchOptions = SearchOption.TopDirectoryOnly, StaticZipOptions zipOptions = StaticZipOptions.None) { Covenant.Requires <ArgumentNullException>(zipStream != null, nameof(zipStream)); using (var zip = ZipFile.Create(zipStream)) { zip.BeginUpdate(); foreach (var file in directory.GetFiles(searchPattern, searchOptions)) { var relativePath = file.Path.Substring(directory.Path.Length + 1); if ((zipOptions | StaticZipOptions.LinuxLineEndings) != 0) { var text = file.ReadAllText(Encoding.UTF8); text = NeonHelper.ToLinuxLineEndings(text); zip.Add(new StaticBytesDataSource(Encoding.UTF8.GetBytes(text)), relativePath); } else { zip.Add(new StaticBytesDataSource(file.ReadAllBytes()), relativePath); } } zip.CommitUpdate(); } }
/// <summary> /// Constructs an instance that loads scripts from embedded resources. /// </summary> /// <param name="masterConnection"> /// The master database connection to be used for creating the target database. This connection must have been made for a Postgres /// superuser or a user with the <b>CREATEDB</b> privilege and must not reference a specific database. /// </param> /// <param name="databaseName">The database name to be used.</param> /// <param name="schemaDirectory">The embedded resource directory returned by a call to <see cref="NeonAssemblyExtensions.GetResourceFileSystem(Assembly, string)"/>.</param> /// <param name="variables">Optionally specifies script variables.</param> /// <exception cref="FileNotFoundException"> /// Thrown if there's no directory at <see cref="scriptFolder"/> or when there's no /// <b>schema-0.script</b> file in the directory. /// </exception> public SchemaManager(NpgsqlConnection masterConnection, string databaseName, IStaticDirectory schemaDirectory, Dictionary <string, string> variables = null) { Covenant.Requires <ArgumentNullException>(masterConnection != null, nameof(masterConnection)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(databaseName), nameof(databaseName)); Covenant.Requires <ArgumentNullException>(schemaDirectory != null, nameof(schemaDirectory)); this.masterConnection = masterConnection; this.targetConnection = null; this.databaseName = databaseName; this.scriptFolder = schemaDirectory.Path; // Initialize the variables dictionary. if (variables != null) { foreach (var item in variables) { this.variables[item.Key] = item.Value; } } this.variables["database"] = databaseName; // List the script files and load them into a dictionary keyed by the schema version // parsed from the file name. We'll also check for duplicate schema files that differ // only by leading zeros in the name. var versionToScript = new Dictionary <int, string>(); var scriptNameRegex = new Regex(@"schema-(?<version>\d+).script$"); foreach (var scriptFile in schemaDirectory.GetFiles("*.script")) { var scriptName = scriptFile.Name; var match = scriptNameRegex.Match(scriptName); if (!match.Success) { throw new SchemaManagerException($"Unexpected script file [{scriptFile}]."); } var scriptVersion = int.Parse(match.Groups["version"].Value); if (versionToScript.ContainsKey(scriptVersion)) { throw new SchemaManagerException($"There are multiple script files for schema [version={scriptVersion}]."); } versionToScript.Add(scriptVersion, LoadScript(scriptFile)); } if (!versionToScript.ContainsKey(0)) { throw new FileNotFoundException($"[schema-0.script] database creation script not found in: {Path.GetDirectoryName(scriptFolder)}"); } this.versionToScript = versionToScript; }
/// <summary> /// Creates a ZIP file, including the selected files from the static directory. /// </summary> /// <param name="directory">The static directory instance.</param> /// <param name="zipPath">Path to the output ZIP file.</param> /// <param name="searchPattern"> /// Optionally specifies a file name pattern using standard file system wildcards /// like <b>[*]</b> and <b>[?]</b>. This defaults to including all files. /// </param> /// <param name="searchOptions">Optionally perform a recursive search. This defaults to /// <see cref="SearchOption.TopDirectoryOnly"/>. /// </param> /// <param name="zipOptions"> /// Additional options that control things like whether the files are zipped within /// the parent directory or whether the files are assumed to contain UTF-8 text and /// that Windows style CRLF line endings are to be converted to Linux compatible LF /// endings. You can combine options by bitwise ORing them. This defaults to /// <see cref="StaticZipOptions.None"/>. /// </param> public static void Zip( this IStaticDirectory directory, string zipPath, string searchPattern = null, SearchOption searchOptions = SearchOption.TopDirectoryOnly, StaticZipOptions zipOptions = StaticZipOptions.None) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(zipPath), nameof(zipPath)); using (var stream = new FileStream(zipPath, FileMode.CreateNew, FileAccess.ReadWrite)) { Zip(directory, stream, searchPattern, searchOptions, zipOptions); } }
/// <summary> /// Initializes a new instance of the DefaultPathUtility class. /// </summary> /// <param name="staticPath">Static path.</param> /// <param name="staticDirectory">Static directory.</param> /// <param name="staticFile">Static file.</param> public DefaultPathUtility(IStaticPath staticPath, IStaticDirectory staticDirectory, IStaticFile staticFile) { this.staticPath = staticPath; this.staticDirectory = staticDirectory; this.staticFile = staticFile; }