Skip hot-reloading if build fails (#480)

* Send error from plugin

* revert loglevel change
This commit is contained in:
omochimetaru 2024-06-08 13:27:32 +08:00 committed by GitHub
parent f734c39ed8
commit 8c7ac43e79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 22 deletions

View File

@ -109,13 +109,16 @@ struct CartonDevPluginCommand: CommandPlugin {
while let _ = try buildRequestFileHandle.read(upToCount: 1) {
Diagnostics.remark("[Plugin] Received build request")
let buildResult = try self.packageManager.build(buildSubset, parameters: parameters)
let responseMessage: Data
if !buildResult.succeeded {
Diagnostics.remark("[Plugin] **Build Failed**")
print(buildResult.logText)
print(buildResult.logText, terminator: "")
responseMessage = Data([0])
} else {
Diagnostics.remark("[Plugin] **Build Succeeded**")
responseMessage = Data([1])
}
try buildResponseFileHandle.write(contentsOf: Data([1]))
try buildResponseFileHandle.write(contentsOf: responseMessage)
}
frontend.waitUntilExit()

View File

@ -17,23 +17,27 @@ import CartonHelpers
import CartonKit
import Foundation
struct CartonFrontendDevCommand: AsyncParsableCommand {
enum Error: Swift.Error & CustomStringConvertible {
case noBuildRequestOption
case noBuildResponseOption
case failedToOpenBuildRequestPipe
case failedToOpenBuildResponsePipe
enum DevCommandError: Error & CustomStringConvertible {
case noBuildRequestOption
case noBuildResponseOption
case failedToOpenBuildRequestPipe
case failedToOpenBuildResponsePipe
case pluginConnectionClosed
case brokenPluginResponse
var description: String {
switch self {
case .noBuildRequestOption: "--build-request option is necessary if you want to watch, but has not been specified."
case .noBuildResponseOption: "--build-response option is necessary if you want to watch, but has not been specified."
case .failedToOpenBuildRequestPipe: "failed to open build request pipe"
case .failedToOpenBuildResponsePipe: "failed to open build response pipe"
}
var description: String {
switch self {
case .noBuildRequestOption: "--build-request option is necessary if you want to watch, but has not been specified."
case .noBuildResponseOption: "--build-response option is necessary if you want to watch, but has not been specified."
case .failedToOpenBuildRequestPipe: "failed to open build request pipe."
case .failedToOpenBuildResponsePipe: "failed to open build response pipe."
case .pluginConnectionClosed: "connection with the plugin has been closed."
case .brokenPluginResponse: "response from the plugin was broken."
}
}
}
struct CartonFrontendDevCommand: AsyncParsableCommand {
static let entrypoint = Entrypoint(fileName: "dev.js", content: StaticResource.dev)
@Option(help: "Specify name of an executable product in development.")
@ -126,10 +130,10 @@ struct CartonFrontendDevCommand: AsyncParsableCommand {
}
guard let buildRequest else {
throw Error.noBuildRequestOption
throw DevCommandError.noBuildRequestOption
}
guard let buildResponse else {
throw Error.noBuildResponseOption
throw DevCommandError.noBuildResponseOption
}
let pathsToWatch = try watchPaths.map {
@ -137,10 +141,10 @@ struct CartonFrontendDevCommand: AsyncParsableCommand {
}
guard let buildRequest = FileHandle(forWritingAtPath: buildRequest) else {
throw Error.failedToOpenBuildRequestPipe
throw DevCommandError.failedToOpenBuildRequestPipe
}
guard let buildResponse = FileHandle(forReadingAtPath: buildResponse) else {
throw Error.failedToOpenBuildResponsePipe
throw DevCommandError.failedToOpenBuildResponsePipe
}
return SwiftPMPluginBuilder(
@ -202,6 +206,20 @@ struct SwiftPMPluginBuilder: BuilderProtocol {
func run() async throws {
// We expect single response per request
try buildRequest.write(contentsOf: Data([1]))
_ = try buildResponse.read(upToCount: 1)
guard let responseMessage = try buildResponse.read(upToCount: 1) else {
throw DevCommandError.pluginConnectionClosed
}
if responseMessage.count < 1 {
throw DevCommandError.brokenPluginResponse
}
switch responseMessage[0] {
case 0:
throw BuilderProtocolSimpleBuildFailedError()
case 1:
// build succeeded
return
default:
throw DevCommandError.brokenPluginResponse
}
}
}

View File

@ -63,6 +63,10 @@ extension Event: Decodable {
}
}
public struct BuilderProtocolSimpleBuildFailedError: Error {
public init() {}
}
/// A protocol for a builder that can be used to build the app.
public protocol BuilderProtocol {
var pathsToWatch: [AbsolutePath] { get }
@ -392,9 +396,19 @@ public actor Server {
_ builder: any BuilderProtocol,
_ terminal: InteractiveWriter
) async throws {
try await builder.run()
do {
try await builder.run()
} catch {
terminal.write("Build failed\n", inColor: .red)
switch error {
case is BuilderProtocolSimpleBuildFailedError: break
default:
terminal.write("\(error)\n", inColor: .red)
}
return
}
terminal.write("\nBuild completed successfully\n", inColor: .green, bold: false)
terminal.write("Build completed successfully\n", inColor: .green)
terminal.logLookup("The app is currently hosted at ", localURL)
connections.forEach { $0.reload() }
}