/+ dub.json: { "name": "issue2234_copy_read_only_files" } +/ /* When DUB copies read-only files to the targetPath, the read-only flag must be removed. If not, any subsequent copy operations will fail. Version control systems such as Git Large File Storage typically mark binary files as read-only, to prevent simultaneous edits in unmergeable formats. */ module issue2234_copy_read_only_files.script; import std.algorithm.searching, std.algorithm.iteration, std.stdio, std.process, std.path, std.file; int main() { const project_dir = buildPath(__FILE_FULL_PATH__.dirName, "issue2234-copy-read-only-files"); const deployment_dir = buildPath(project_dir, "bin"); auto deployables = dirEntries(buildPath(project_dir, "files"), "*", SpanMode.depth).filter!isFile; // Prepare environment. if (deployment_dir.exists) { foreach (entry; dirEntries(deployment_dir, "*", SpanMode.depth)) { if (entry.isDir) entry.rmdir; else { entry.makeWritable; entry.remove; } } deployment_dir.rmdir; } foreach (ref f; deployables) f.makeReadOnly; // Execute test. const dub = environment.get("DUB", buildPath(__FILE_FULL_PATH__.dirName.dirName, "bin", "dub.exe")); const cmd = [dub, "build", "--build=release"]; const result = execute(cmd, null, Config.none, size_t.max, project_dir); if (result.status || result.output.canFind("Failed")) { writefln("\n> %-(%s %)", cmd); writeln("==========================================================="); writeln(result.output); writeln("==========================================================="); writeln("Last command failed with exit code ", result.status, '\n'); return 1; } foreach (deployed; dirEntries(deployment_dir, "*", SpanMode.depth).filter!isFile) if (!isWritable(deployed)) { writeln(deployed, " is expected to be writable, but it is not."); return 1; } return 0; } void makeReadOnly(string name) { version (Windows) { import core.sys.windows.windows; name.setAttributes(name.getAttributes() | FILE_ATTRIBUTE_READONLY); } else version (Posix) { import core.sys.posix.sys.stat; name.setAttributes(name.getAttributes() & ~(S_IWUSR | S_IWGRP | S_IWOTH)); } else static assert("Needs implementation."); import std.exception; import std.stdio; assertThrown!ErrnoException(File(name, "w")); } void makeWritable(string name) { version (Windows) { import core.sys.windows.windows; name.setAttributes(name.getAttributes() & ~FILE_ATTRIBUTE_READONLY); } else version (Posix) { import core.sys.posix.sys.stat; name.setAttributes(name.getAttributes() | S_IWUSR); } else static assert("Needs implementation."); import std.exception; import std.stdio; assertNotThrown!ErrnoException(File(name, "w")); } bool isWritable(string name) { version (Windows) { import core.sys.windows.windows; return (name.getAttributes() & FILE_ATTRIBUTE_READONLY) == 0; } else version (Posix) { import core.sys.posix.sys.stat; return (name.getAttributes() & S_IWUSR) != 0; } else static assert("Needs implementation."); }