Skip to content

Commit

Permalink
Skip hot-reloading if build fails (#480)
Browse files Browse the repository at this point in the history
* Send error from plugin

* revert loglevel change
  • Loading branch information
omochi committed Jun 8, 2024
1 parent f734c39 commit 8c7ac43
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 23 deletions.
7 changes: 5 additions & 2 deletions Plugins/CartonDevPlugin/CartonDevPluginCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
56 changes: 37 additions & 19 deletions Sources/CartonFrontend/Commands/CartonFrontendDevCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

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"
}
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."
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.")
Expand Down Expand Up @@ -126,21 +130,21 @@ 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 {
try AbsolutePath(validating: $0, relativeTo: localFileSystem.currentWorkingDirectory!)
}

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(
Expand Down Expand Up @@ -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
}
}
}
18 changes: 16 additions & 2 deletions Sources/CartonKit/Server/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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() }
}
Expand Down

0 comments on commit 8c7ac43

Please sign in to comment.