(argv: Arguments)
| 122 | * response is executed. |
| 123 | */ |
| 124 | export async function captureLogOutputForCommand(argv: Arguments) { |
| 125 | // TODO(josephperrott): remove this guard against running multiple times after |
| 126 | // https://github.com/yargs/yargs/issues/2223 is fixed |
| 127 | if (logStream !== undefined) { |
| 128 | return; |
| 129 | } |
| 130 | const repoDir = determineRepoBaseDirFromCwd(); |
| 131 | const logFilePath = join(repoDir, '.ng-dev.log'); |
| 132 | if (existsSync(logFilePath) && lstatSync(logFilePath).isSymbolicLink()) { |
| 133 | throw new Error( |
| 134 | 'Security Violation: .ng-dev.log is a symbolic link. ' + |
| 135 | 'To prevent arbitrary file write, execution is aborted.', |
| 136 | ); |
| 137 | } |
| 138 | logStream = createWriteStream(logFilePath, {encoding: 'utf-8'}); |
| 139 | |
| 140 | /** The date time used for timestamping when the command was invoked. */ |
| 141 | const now = new Date(); |
| 142 | /** Header line to separate command runs in log files. */ |
| 143 | const headerLine = Array(100).fill('#').join(''); |
| 144 | appendToLogFile( |
| 145 | undefined, |
| 146 | `${headerLine}\nCommand: ${argv.$0} ${argv._.join(' ')}\nRan at: ${now}\n`, |
| 147 | ); |
| 148 | |
| 149 | // When the command is completed, write the logged output to the appropriate log files |
| 150 | registerCompletedFunction(async (error) => { |
| 151 | if (error instanceof Error) { |
| 152 | appendToLogFile(LogLevel.ERROR, error.message); |
| 153 | const stack = error.stack?.split('\n') ?? []; |
| 154 | for (const line of stack) { |
| 155 | appendToLogFile(LogLevel.ERROR, line); |
| 156 | } |
| 157 | } |
| 158 | appendToLogFile( |
| 159 | undefined, |
| 160 | `\n\nCommand ran in ${new Date().getTime() - now.getTime()}ms\nExit Code: ${process.exitCode}\n`, |
| 161 | ); |
| 162 | logStream!.end(() => { |
| 163 | // For failure codes greater than 1, the new logged lines should be written to a specific log |
| 164 | // file for the command run failure. |
| 165 | if (process.exitCode !== 0) { |
| 166 | const errorLogFileName = `.ng-dev.err-${now.getTime()}.log`; |
| 167 | console.error( |
| 168 | `Exit code: ${process.exitCode}.\nWriting full log to ${join(repoDir, errorLogFileName)}`, |
| 169 | ); |
| 170 | copyFileSync(logFilePath, join(repoDir, errorLogFileName)); |
| 171 | } |
| 172 | }); |
| 173 | }); |
| 174 | } |
| 175 | |
| 176 | /** Write the provided text to the log file, prepending each line with the log level. */ |
| 177 | function appendToLogFile(logLevel: LogLevel | undefined, ...text: unknown[]) { |
nothing calls this directly
no test coverage detected