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) { while let _ = try buildRequestFileHandle.read(upToCount: 1) {
Diagnostics.remark("[Plugin] Received build request") Diagnostics.remark("[Plugin] Received build request")
let buildResult = try self.packageManager.build(buildSubset, parameters: parameters) let buildResult = try self.packageManager.build(buildSubset, parameters: parameters)
let responseMessage: Data
if !buildResult.succeeded { if !buildResult.succeeded {
Diagnostics.remark("[Plugin] **Build Failed**") Diagnostics.remark("[Plugin] **Build Failed**")
print(buildResult.logText) print(buildResult.logText, terminator: "")
responseMessage = Data([0])
} else { } else {
Diagnostics.remark("[Plugin] **Build Succeeded**") Diagnostics.remark("[Plugin] **Build Succeeded**")
responseMessage = Data([1])
} }
try buildResponseFileHandle.write(contentsOf: Data([1])) try buildResponseFileHandle.write(contentsOf: responseMessage)
} }
frontend.waitUntilExit() frontend.waitUntilExit()

View File

@ -17,23 +17,27 @@ import CartonHelpers
import CartonKit import CartonKit
import Foundation import Foundation
struct CartonFrontendDevCommand: AsyncParsableCommand { enum DevCommandError: Error & CustomStringConvertible {
enum Error: Swift.Error & CustomStringConvertible {
case noBuildRequestOption case noBuildRequestOption
case noBuildResponseOption case noBuildResponseOption
case failedToOpenBuildRequestPipe case failedToOpenBuildRequestPipe
case failedToOpenBuildResponsePipe case failedToOpenBuildResponsePipe
case pluginConnectionClosed
case brokenPluginResponse
var description: String { var description: String {
switch self { switch self {
case .noBuildRequestOption: "--build-request option is necessary if you want to watch, but has not been specified." 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 .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 .failedToOpenBuildRequestPipe: "failed to open build request pipe."
case .failedToOpenBuildResponsePipe: "failed to open build response 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) static let entrypoint = Entrypoint(fileName: "dev.js", content: StaticResource.dev)
@Option(help: "Specify name of an executable product in development.") @Option(help: "Specify name of an executable product in development.")
@ -126,10 +130,10 @@ struct CartonFrontendDevCommand: AsyncParsableCommand {
} }
guard let buildRequest else { guard let buildRequest else {
throw Error.noBuildRequestOption throw DevCommandError.noBuildRequestOption
} }
guard let buildResponse else { guard let buildResponse else {
throw Error.noBuildResponseOption throw DevCommandError.noBuildResponseOption
} }
let pathsToWatch = try watchPaths.map { let pathsToWatch = try watchPaths.map {
@ -137,10 +141,10 @@ struct CartonFrontendDevCommand: AsyncParsableCommand {
} }
guard let buildRequest = FileHandle(forWritingAtPath: buildRequest) else { guard let buildRequest = FileHandle(forWritingAtPath: buildRequest) else {
throw Error.failedToOpenBuildRequestPipe throw DevCommandError.failedToOpenBuildRequestPipe
} }
guard let buildResponse = FileHandle(forReadingAtPath: buildResponse) else { guard let buildResponse = FileHandle(forReadingAtPath: buildResponse) else {
throw Error.failedToOpenBuildResponsePipe throw DevCommandError.failedToOpenBuildResponsePipe
} }
return SwiftPMPluginBuilder( return SwiftPMPluginBuilder(
@ -202,6 +206,20 @@ struct SwiftPMPluginBuilder: BuilderProtocol {
func run() async throws { func run() async throws {
// We expect single response per request // We expect single response per request
try buildRequest.write(contentsOf: Data([1])) 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. /// A protocol for a builder that can be used to build the app.
public protocol BuilderProtocol { public protocol BuilderProtocol {
var pathsToWatch: [AbsolutePath] { get } var pathsToWatch: [AbsolutePath] { get }
@ -392,9 +396,19 @@ public actor Server {
_ builder: any BuilderProtocol, _ builder: any BuilderProtocol,
_ terminal: InteractiveWriter _ terminal: InteractiveWriter
) async throws { ) async throws {
do {
try await builder.run() 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) terminal.logLookup("The app is currently hosted at ", localURL)
connections.forEach { $0.reload() } connections.forEach { $0.reload() }
} }