Write all logs to disk
This commit is contained in:
@@ -3,7 +3,7 @@ import Foundation
|
||||
struct FileData {
|
||||
|
||||
///The files marked as expected, i.e. they exist after the generation is completed. (`key`: file path, `value`: the file providing the link)
|
||||
var expected: [String : String] = [:]
|
||||
var expected: [String : String] = [:]
|
||||
|
||||
/// All files which should be copied to the output folder (`key`: The file path, `value`: The source requiring the file)
|
||||
var toCopy: [String : String] = [:]
|
||||
|
@@ -157,9 +157,10 @@ final class FileGenerator {
|
||||
command = "cleancss \(url.path) -o \(tempFile.path)"
|
||||
}
|
||||
|
||||
try? tempFile.delete()
|
||||
|
||||
defer { didMinifyFile() }
|
||||
defer {
|
||||
didMinifyFile()
|
||||
try? tempFile.delete()
|
||||
}
|
||||
|
||||
let output: String
|
||||
do {
|
||||
@@ -256,4 +257,30 @@ final class FileGenerator {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeResults(to file: URL) {
|
||||
var lines: [String] = []
|
||||
func add<S>(_ name: String, _ property: S, convert: (S.Element) -> String) where S: Sequence {
|
||||
let elements = property.map { " " + convert($0) }.sorted()
|
||||
guard !elements.isEmpty else {
|
||||
return
|
||||
}
|
||||
lines.append("\(name):")
|
||||
lines.append(contentsOf: elements)
|
||||
}
|
||||
add("Unreadable files", unreadableFiles) { "\($0.key) (required by \($0.value.source)): \($0.value.message)" }
|
||||
add("Unwritable files", unwritableFiles) { "\($0.key) (required by \($0.value.source)): \($0.value.message)" }
|
||||
add("Failed minifications", failedMinifications) { "\($0.file) (required by \($0.source)): \($0.message)" }
|
||||
add("Missing files", missingFiles) { "\($0.key) (required by \($0.value))" }
|
||||
add("Copied files", copiedFiles) { $0 }
|
||||
add("Minified files", minifiedFiles) { $0 }
|
||||
add("Expected files", files.expected) { "\($0.key) (required by \($0.value))" }
|
||||
|
||||
let data = lines.joined(separator: "\n").data(using: .utf8)!
|
||||
do {
|
||||
try data.createFolderAndWrite(to: file)
|
||||
} catch {
|
||||
print(" Failed to save log: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -441,7 +441,7 @@ final class GenerationResultsHandler {
|
||||
print("")
|
||||
}
|
||||
|
||||
func writeResultsToFile(file: URL) throws {
|
||||
func writeResults(to file: URL) {
|
||||
var lines: [String] = []
|
||||
func add<S>(_ name: String, _ property: S, convert: (S.Element) -> String) where S: Sequence {
|
||||
let elements = property.map { " " + convert($0) }.sorted()
|
||||
@@ -459,9 +459,12 @@ final class GenerationResultsHandler {
|
||||
add("Drafts", draftPages) { $0 }
|
||||
add("Empty pages", emptyPages) { "\($0.key) (from \($0.value))" }
|
||||
add("Generated files", generatedFiles) { $0 }
|
||||
|
||||
let data = lines.joined(separator: "\n").data(using: .utf8)!
|
||||
try data.createFolderAndWrite(to: file)
|
||||
do {
|
||||
try data.createFolderAndWrite(to: file)
|
||||
} catch {
|
||||
print(" Failed to save log: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,18 +24,24 @@ final class ImageGenerator {
|
||||
/**
|
||||
All warnings produced for images during generation
|
||||
*/
|
||||
private var imageWarnings: Set<String> = []
|
||||
private var imageWarnings: [String]
|
||||
|
||||
/**
|
||||
All images modified or created during this generator run.
|
||||
*/
|
||||
/// The images which could not be found, but are required for the site (`key`: image path, `value`: source element path)
|
||||
private var missingImages: [String : String]
|
||||
|
||||
/// Images which could not be read (`key`: file path relative to content, `value`: source element path)
|
||||
private var unreadableImages: [String : String]
|
||||
|
||||
private var unhandledImages: [String: String] = [:]
|
||||
|
||||
/// All images modified or created during this generator run.
|
||||
private var generatedImages: Set<String> = []
|
||||
|
||||
/**
|
||||
The images optimized by ImageOptim
|
||||
*/
|
||||
/// The images optimized by ImageOptim
|
||||
private var optimizedImages: Set<String> = []
|
||||
|
||||
private var failedImages: [(path: String, message: String)] = []
|
||||
|
||||
private var numberOfGeneratedImages = 0
|
||||
|
||||
private let numberOfTotalImages: Int
|
||||
@@ -71,6 +77,9 @@ final class ImageGenerator {
|
||||
self.images = images
|
||||
self.numberOfTotalImages = images.jobs.reduce(0) { $0 + $1.value.count }
|
||||
+ images.multiJobs.count * 2
|
||||
self.imageWarnings = images.warnings
|
||||
self.missingImages = images.missing
|
||||
self.unreadableImages = images.unreadable
|
||||
}
|
||||
|
||||
func generateImages() {
|
||||
@@ -81,15 +90,10 @@ final class ImageGenerator {
|
||||
}
|
||||
notes.append("\(count) \(name)")
|
||||
}
|
||||
addIfNotZero(images.missing.count, "missing images")
|
||||
addIfNotZero(images.unreadable.count, "unreadable images")
|
||||
|
||||
print(" Changed sources: \(jobs.count)/\(images.jobs.count)")
|
||||
print(" Total images: \(numberOfTotalImages) (\(numberOfTotalImages - imageReader.numberOfFilesAccessed) versions)")
|
||||
print(" Warnings: \(images.warnings.count)")
|
||||
if !notes.isEmpty {
|
||||
print(" Notes: " + notes.joined(separator: ", "))
|
||||
}
|
||||
|
||||
for (source, jobs) in jobs {
|
||||
create(images: jobs, from: source)
|
||||
}
|
||||
@@ -99,11 +103,18 @@ final class ImageGenerator {
|
||||
print(" Generated images: \(numberOfGeneratedImages)/\(numberImagesToCreate)")
|
||||
optimizeImages()
|
||||
print(" Optimized images: \(numberOfOptimizedImages)/\(numberOfImagesToOptimize)")
|
||||
|
||||
addIfNotZero(missingImages.count, "missing images")
|
||||
addIfNotZero(unreadableImages.count, "unreadable images")
|
||||
print(" Warnings: \(imageWarnings.count)")
|
||||
if !notes.isEmpty {
|
||||
print(" Notes: " + notes.joined(separator: ", "))
|
||||
}
|
||||
}
|
||||
|
||||
private func create(images: [ImageJob], from source: String) {
|
||||
guard let image = imageReader.getImage(atPath: source) else {
|
||||
// TODO: Add to failed images
|
||||
unreadableImages[source] = images.first!.destination
|
||||
didGenerateImage(count: images.count)
|
||||
return
|
||||
}
|
||||
@@ -137,7 +148,7 @@ final class ImageGenerator {
|
||||
|
||||
let destinationExtension = destinationUrl.pathExtension.lowercased()
|
||||
guard let type = ImageType(fileExtension: destinationExtension)?.fileType else {
|
||||
addWarning("Invalid image extension \(destinationExtension)", job: job)
|
||||
unhandledImages[source] = job.destination
|
||||
return
|
||||
}
|
||||
|
||||
@@ -168,25 +179,20 @@ final class ImageGenerator {
|
||||
|
||||
// Get NSData, and save it
|
||||
guard let data = rep.representation(using: type, properties: [.compressionFactor: NSNumber(value: job.quality)]) else {
|
||||
addWarning("Failed to get data", job: job)
|
||||
markImageAsFailed(source, error: "Failed to get data")
|
||||
return
|
||||
}
|
||||
do {
|
||||
try data.createFolderAndWrite(to: destinationUrl)
|
||||
} catch {
|
||||
addWarning("Failed to write image (\(error))", job: job)
|
||||
markImageAsFailed(job.destination, error: "Failed to write image (\(error))")
|
||||
return
|
||||
}
|
||||
generatedImages.insert(job.destination)
|
||||
}
|
||||
|
||||
private func addWarning(_ message: String, destination: String, path: String) {
|
||||
let warning = " \(destination): \(message) required by \(path)"
|
||||
imageWarnings.insert(warning)
|
||||
}
|
||||
|
||||
private func addWarning(_ message: String, job: ImageJob) {
|
||||
addWarning(message, destination: job.destination, path: job.path)
|
||||
private func markImageAsFailed(_ source: String, error: String) {
|
||||
failedImages.append((source, error))
|
||||
}
|
||||
|
||||
private func createMultiImages(from source: String, path: String) {
|
||||
@@ -198,7 +204,7 @@ final class ImageGenerator {
|
||||
let sourceUrl = output.appendingPathComponent(source)
|
||||
let sourcePath = sourceUrl.path
|
||||
guard sourceUrl.exists else {
|
||||
addWarning("No image at path \(sourcePath)", destination: source, path: path)
|
||||
missingImages[source] = path
|
||||
didGenerateImage(count: 2)
|
||||
return
|
||||
}
|
||||
@@ -222,7 +228,7 @@ final class ImageGenerator {
|
||||
do {
|
||||
_ = try safeShell(command)
|
||||
} catch {
|
||||
addWarning("Failed to create AVIF image", destination: destination, path: destination)
|
||||
markImageAsFailed(destination, error: "Failed to create AVIF image")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +237,7 @@ final class ImageGenerator {
|
||||
do {
|
||||
_ = try safeShell(command)
|
||||
} catch {
|
||||
addWarning("Failed to create WEBP image", destination: destination, path: destination)
|
||||
markImageAsFailed(destination, error: "Failed to create WEBP image")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +246,7 @@ final class ImageGenerator {
|
||||
do {
|
||||
_ = try safeShell(command)
|
||||
} catch {
|
||||
addWarning("Failed to compress image", destination: destination, path: destination)
|
||||
markImageAsFailed(destination, error: "Failed to compress image")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +270,9 @@ final class ImageGenerator {
|
||||
_ = try safeShell(command)
|
||||
return true
|
||||
} catch {
|
||||
addWarning("Failed to optimize images", destination: "", path: "")
|
||||
for image in batch {
|
||||
markImageAsFailed(image, error: "Failed to optimize image")
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -282,4 +290,29 @@ final class ImageGenerator {
|
||||
print(" Optimized images: \(numberOfOptimizedImages)/\(numberOfImagesToOptimize) \r", terminator: "")
|
||||
fflush(stdout)
|
||||
}
|
||||
|
||||
func writeResults(to file: URL) {
|
||||
var lines: [String] = []
|
||||
func add<S>(_ name: String, _ property: S, convert: (S.Element) -> String) where S: Sequence {
|
||||
let elements = property.map { " " + convert($0) }.sorted()
|
||||
guard !elements.isEmpty else {
|
||||
return
|
||||
}
|
||||
lines.append("\(name):")
|
||||
lines.append(contentsOf: elements)
|
||||
}
|
||||
add("Missing images", missingImages) { "\($0.key) (required by \($0.value))" }
|
||||
add("Unreadable images", unreadableImages) { "\($0.key) (required by \($0.value))" }
|
||||
add("Failed images", failedImages) { "\($0.path): \($0.message)" }
|
||||
add("Unhandled images", unhandledImages) { "\($0.value) (from \($0.key))" }
|
||||
add("Warnings", imageWarnings) { $0 }
|
||||
add("Generated images", generatedImages) { $0 }
|
||||
add("Optimized images", optimizedImages) { $0 }
|
||||
let data = lines.joined(separator: "\n").data(using: .utf8)!
|
||||
do {
|
||||
try data.createFolderAndWrite(to: file)
|
||||
} catch {
|
||||
print(" Failed to save log: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,6 @@ final class MetadataInfoLogger {
|
||||
|
||||
private let input: URL
|
||||
|
||||
private let runFolder: URL
|
||||
|
||||
private var numberOfMetadataFiles = 0
|
||||
|
||||
private var unusedProperties: [(name: String, source: String)] = []
|
||||
@@ -22,13 +20,8 @@ final class MetadataInfoLogger {
|
||||
|
||||
private var errors: [(source: String, message: String)] = []
|
||||
|
||||
private var logFile: URL {
|
||||
runFolder.appendingPathComponent("Metadata issues.txt")
|
||||
}
|
||||
|
||||
init(input: URL, runFolder: URL) {
|
||||
init(input: URL) {
|
||||
self.input = input
|
||||
self.runFolder = runFolder
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +124,7 @@ final class MetadataInfoLogger {
|
||||
// MARK: Printing
|
||||
|
||||
private func printMetadataScanUpdate() {
|
||||
print(String(format: " Pages found: %4d \r", numberOfMetadataFiles), terminator: "")
|
||||
print(" Pages found: \(numberOfMetadataFiles) \r", terminator: "")
|
||||
}
|
||||
|
||||
func printMetadataScanOverview(languages: Int) {
|
||||
@@ -157,31 +150,29 @@ final class MetadataInfoLogger {
|
||||
}
|
||||
}
|
||||
|
||||
func writeResultsToFile() throws {
|
||||
func writeResults(to file: URL) {
|
||||
var lines: [String] = []
|
||||
if !errors.isEmpty {
|
||||
lines += ["Errors:"] + errors.map { "\($0.source): \($0.message)" }.sorted()
|
||||
}
|
||||
if !warnings.isEmpty {
|
||||
lines += ["Warnings:"] + warnings.map { "\($0.source): \($0.message)" }.sorted()
|
||||
}
|
||||
if !unreadableMetadata.isEmpty {
|
||||
lines += ["Unreadable files:"] + unreadableMetadata.map { "\($0.source): \($0.error)" }.sorted()
|
||||
}
|
||||
if !unusedProperties.isEmpty {
|
||||
lines += ["Unused properties:"] + unusedProperties.map { "\($0.source): \($0.name)" }.sorted()
|
||||
}
|
||||
if !invalidProperties.isEmpty {
|
||||
lines += ["Invalid properties:"] + invalidProperties.map { "\($0.source): \($0.name) (\($0.reason))" }.sorted()
|
||||
}
|
||||
if !unknownProperties.isEmpty {
|
||||
lines += ["Unknown properties:"] + unknownProperties.map { "\($0.source): \($0.name)" }.sorted()
|
||||
}
|
||||
if !missingProperties.isEmpty {
|
||||
lines += ["Missing properties:"] + missingProperties.map { "\($0.source): \($0.name)" }.sorted()
|
||||
func add<S>(_ name: String, _ property: S, convert: (S.Element) -> String) where S: Sequence {
|
||||
let elements = property.map { " " + convert($0) }.sorted()
|
||||
guard !elements.isEmpty else {
|
||||
return
|
||||
}
|
||||
lines.append("\(name):")
|
||||
lines.append(contentsOf: elements)
|
||||
}
|
||||
add("Errors", errors) { "\($0.source): \($0.message)" }
|
||||
add("Warnings", warnings) { "\($0.source): \($0.message)" }
|
||||
add("Unreadable files", unreadableMetadata) { "\($0.source): \($0.error)" }
|
||||
add("Unused properties", unusedProperties) { "\($0.source): \($0.name)" }
|
||||
add("Invalid properties", invalidProperties) { "\($0.source): \($0.name) (\($0.reason))" }
|
||||
add("Unknown properties", unknownProperties) { "\($0.source): \($0.name)" }
|
||||
add("Missing properties", missingProperties) { "\($0.source): \($0.name)" }
|
||||
|
||||
let data = lines.joined(separator: "\n").data(using: .utf8)!
|
||||
try data.createFolderAndWrite(to: logFile)
|
||||
do {
|
||||
try data.createFolderAndWrite(to: file)
|
||||
} catch {
|
||||
print(" Failed to save log: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user