1 #!/usr/bin/rdmd 2 3 module ddocs.org.buffered; 4 5 import std.algorithm; 6 import std.file; 7 import std.path: buildPath; 8 import std.stdio; 9 import std.string; 10 11 auto helpString = q"( 12 ------------------------------------------------------------------------------- 13 ddocs.org.buffered 14 Runs ddocs.org in a double-buffered manner. 15 Copyright (C) 2015 Ferdinand Majerech 16 17 Usage: ddocs.org.buffered RESULT WORK [DDOCS.ORG-OPTIONS] 18 19 ddocs.org.buffered runs `ddocs.org`, writing output to the WORK/public 20 directory and then switches RESULT and WORK directories. DDOCS.ORG-OPTIONS are 21 passed to `ddocs.org`. 22 ------------------------------------------------------------------------------- 23 )"; 24 25 int main(string[] args) 26 { 27 if(args.length < 2) 28 { 29 writeln("Need at least 2 arguments"); 30 writeln(helpString); 31 return 0; 32 } 33 34 const resultPath = args[1]; 35 const workPath = args[2]; 36 const ddocsArgs = args[3 .. $]; 37 38 const tempPath = resultPath ~ ".tmp"; 39 // if tempPath exists, we've probably crashed in the middle of a previous exchange. 40 // Abort to avoid any more damage. 41 if(tempPath.exists) 42 { 43 writeln("TEMP PATH '" ~ tempPath ~ "' EXISTS: LEFTOVER FROM PREVIOUS FAILURE? ABORTING"); 44 return 1; 45 } 46 47 try 48 { 49 writeln("ddocs.org.buffered: Ensuring the result/work paths exist"); 50 if(!resultPath.exists) { mkdirRecurse(resultPath); } 51 if(!workPath.exists) { mkdirRecurse(workPath); } 52 53 // Also ensure there's a directory to copy the complete log to. 54 const workLogPath = workPath.buildPath("logs"); 55 if(!workLogPath.exists) { mkdirRecurse(workLogPath); } 56 } 57 catch(Exception e) 58 { 59 writeln("FAILED CREATING PUBLIC AND/OR WORK DIR! ERROR:\n\n", e); 60 return 2; 61 } 62 63 64 auto log = File(workPath.buildPath("ddocs.org.buffered.log"), "a"); 65 void print(S ...)(S args) 66 { 67 writeln(args); 68 log.writeln(args); 69 } 70 import std.datetime; 71 print("---\n", Clock.currTime.toISOExtString(), "\n---"); 72 73 import std.process; 74 writeln("ddocs.org.buffered: generating documentation"); 75 const processArgs = "ddocs.org" ~ ddocsArgs; 76 writeln(processArgs.map!(a => "'%s'".format(a)).joiner(" ")); 77 auto pid = spawnProcess(processArgs, stdin, stdout, stderr, null, Config.none, workPath); 78 auto status = pid.wait; 79 if(status != 0) 80 { 81 print("FAILED TO GENERATE DOCUMENTATION! STATUS: ", status); 82 return 3; 83 } 84 85 writeln("ddocs.org.buffered: archiving documentation log"); 86 import std.datetime; 87 const logName = "ddocs.org-log.yaml"; 88 const logNameXZ = "ddocs.org-log.yaml.xz"; 89 // Remove the archived log if it a 90 const shell = "xz -3f %s && mv %s ./logs/ddocs.org-log.yaml.%s.xz" 91 .format(logName, logNameXZ, Clock.currStdTime); 92 writeln(shell); 93 pid = spawnShell(shell, stdin, stdout, stderr, null, Config.none, workPath); 94 status = pid.wait; 95 if(status != 0) 96 { 97 print("FAILED TO ARCHIVE DOCUMENTATION LOG! STATUS: ", status); 98 return 4; 99 } 100 101 try 102 { 103 writeln("ddocs.org.buffered: switching work and result directories"); 104 resultPath.rename(tempPath); 105 workPath.rename(resultPath); 106 tempPath.rename(workPath); 107 } 108 catch(Exception e) 109 { 110 print("FAILED WORK/RESULT DIRECTORY EXCHANGE! ERROR:\n\n", e); 111 return 5; 112 } 113 114 return 0; 115 }