Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dry run option to CLI #123

Merged
merged 9 commits into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Sources/swift-openapi-generator/GenerateCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,17 @@ struct _GenerateCommand: AsyncParsableCommand {
)
var isPluginInvocation: Bool = false

@Flag(
help:
"Simulate the command and print the operations, without actually affecting the file system."
)
var isDryRun: Bool = false
denil-ct marked this conversation as resolved.
Show resolved Hide resolved

func run() async throws {
try generate.runGenerator(
outputDirectory: outputDirectory,
isPluginInvocation: isPluginInvocation
isPluginInvocation: isPluginInvocation,
isDryRun: isDryRun
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ extension _GenerateOptions {
/// Server.swift) regardless of which generator mode was requested, with
/// the caveat that the not requested files are empty. This is due to
/// a limitation of the build system used by SwiftPM under the hood.
/// - isDryRun: A Boolean value that indicates whether this invocation should
/// be run in a testing mode to preview all the operations being carried out without
/// making any actual changes.
func runGenerator(
outputDirectory: URL,
isPluginInvocation: Bool
isPluginInvocation: Bool,
isDryRun: Bool
) throws {
let config = try loadedConfig()
let sortedModes = try resolvedModes(config)
Expand Down Expand Up @@ -63,6 +67,7 @@ extension _GenerateOptions {
- Diagnostics output path: \(diagnosticsOutputPath?.path ?? "<none - logs to stderr>")
- Current directory: \(FileManager.default.currentDirectoryPath)
- Is plugin invocation: \(isPluginInvocation)
- Is dry run: \(isDryRun)
- Additional imports: \(resolvedAdditionalImports.isEmpty ? "<none>" : resolvedAdditionalImports.joined(separator: ", "))
"""
)
Expand All @@ -72,6 +77,7 @@ extension _GenerateOptions {
configs: configs,
isPluginInvocation: isPluginInvocation,
outputDirectory: outputDirectory,
isDryRun: isDryRun,
diagnostics: diagnostics
)
} catch let error as Diagnostic {
Expand Down
52 changes: 35 additions & 17 deletions Sources/swift-openapi-generator/runGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import ArgumentParser
import _OpenAPIGeneratorCore

extension _Tool {

/// Runs the generator with the specified configuration values.
/// - Parameters:
/// - doc: A path to the OpenAPI document.
Expand All @@ -25,12 +24,15 @@ extension _Tool {
/// generator invocation is coming from a SwiftPM plugin.
/// - outputDirectory: The directory to which the generator writes
/// the generated Swift files.
/// - isDryRun: A Boolean value that indicates whether this invocation should
/// be a dry run.
/// - diagnostics: A collector for diagnostics emitted by the generator.
static func runGenerator(
doc: URL,
configs: [Config],
isPluginInvocation: Bool,
outputDirectory: URL,
isDryRun: Bool,
diagnostics: any DiagnosticCollector
) throws {
let docData: Data
Expand All @@ -48,14 +50,15 @@ extension _Tool {
docData: docData,
config: config,
outputFilePath: filePathForMode(config.mode),
isDryRun: isDryRun,
diagnostics: diagnostics
)
}
if isPluginInvocation {
let nonGeneratedModes = Set(GeneratorMode.allCases).subtracting(configs.map(\.mode))
for mode in nonGeneratedModes.sorted() {
let path = filePathForMode(mode)
try replaceFileContents(at: path, with: { Data() })
try replaceFileContents(at: path, with: { Data() }, isDryRun: isDryRun)
}
}
}
Expand All @@ -67,34 +70,45 @@ extension _Tool {
/// - config: A set of configuration values for the generator.
/// - outputFilePath: The directory to which the generator writes
/// the generated Swift files.
/// - isDryRun: A Boolean value that indicates whether this invocation should
/// be a dry run.
/// - diagnostics: A collector for diagnostics emitted by the generator.
static func runGenerator(
doc: URL,
docData: Data,
config: Config,
outputFilePath: URL,
isDryRun: Bool,
diagnostics: any DiagnosticCollector
) throws {
let didChange = try replaceFileContents(at: outputFilePath) {
let output = try _OpenAPIGeneratorCore.runGenerator(
input: .init(absolutePath: doc, contents: docData),
config: config,
diagnostics: diagnostics
)
return output.contents
}
print("File \(outputFilePath.lastPathComponent): \(didChange ? "changed" : "unchanged")")
denil-ct marked this conversation as resolved.
Show resolved Hide resolved
try replaceFileContents(
at: outputFilePath,
with: {
let output = try _OpenAPIGeneratorCore.runGenerator(
input: .init(absolutePath: doc, contents: docData),
config: config,
diagnostics: diagnostics
)
return output.contents
},
isDryRun: isDryRun
)
}

/// Evaluates a closure to generate file data and writes the data to disk
/// if the data is different than the current file contents.
/// if the data is different than the current file contents. Will write to disk
/// only if `isDryRun` is set as `false`.
/// - Parameters:
/// - path: A path to the file.
/// - contents: A closure evaluated to produce the file contents data.
/// - isDryRun: A Boolean value that indicates whether this invocation should
/// be a dry run. File system changes will not be written to disk in this mode.
/// - Throws: When writing to disk fails.
/// - Returns: `true` if the generated contents changed, otherwise `false`.
@discardableResult
static func replaceFileContents(at path: URL, with contents: () throws -> Data) throws -> Bool {
static func replaceFileContents(
at path: URL,
with contents: () throws -> Data,
isDryRun: Bool
) throws {
let data = try contents()
let didChange: Bool
if FileManager.default.fileExists(atPath: path.path) {
Expand All @@ -104,8 +118,12 @@ extension _Tool {
didChange = true
}
if didChange {
try data.write(to: path)
print("File \(path.lastPathComponent) will be overwritten.")
if !isDryRun {
try data.write(to: path)
}
denil-ct marked this conversation as resolved.
Show resolved Hide resolved
} else {
print("File \(path.lastPathComponent) will remain unchanged.")
}
return didChange
}
denil-ct marked this conversation as resolved.
Show resolved Hide resolved
}