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 --host option to docc preview #713

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,10 @@ Monitoring ~/Developer/swift-docc/Sources/SwiftDocC/SwiftDocC.docc for changes..
```

And if you navigate to <http://localhost:8080/documentation/swiftdocc> you'll see
the rendered documentation for `SwiftDocC`.
the rendered documentation for `SwiftDocC`. If your browser isn't running on
the same computer as Swift-DocC, you may need to provide the server's public
host name or IP address (or just `0.0.0.0`) with the `--host` option to allow
the browser to connect.

### Using Docker to Test Swift-DocC for Linux

Expand Down
11 changes: 8 additions & 3 deletions Sources/SwiftDocCUtilities/Action/Actions/PreviewAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public final class PreviewAction: Action, RecreatingContext {

var logHandle = LogHandle.standardOutput

let host: String
let port: Int

var convertAction: ConvertAction
Expand All @@ -75,6 +76,7 @@ public final class PreviewAction: Action, RecreatingContext {
/// Creates a new preview action from the given parameters.
///
/// - Parameters:
/// - host: The host name used by the preview server.
/// - port: The port number used by the preview server.
/// - convertAction: The action used to convert the documentation bundle before preview.
/// On macOS, this action will be reused to convert documentation each time the source is modified.
Expand All @@ -84,6 +86,7 @@ public final class PreviewAction: Action, RecreatingContext {
/// is performed.
/// - Throws: If an error is encountered while initializing the documentation context.
public init(
host: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a public initializer, providing a default value here would avoid a source breaking change for anyone who imports SourceDocCUtilities.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. duplicate the original init and add host parameter.
  2. mark the original init as deprecate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I commented here I think it's sufficient to provide a default value for this added parameter to remain source compatible. It won't be ABI compatible but we don't require that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Ethan's PR #254 is removing parameters. So he deprecated the old and add a new one.
For this PR, this is just an addition. If we only cares about source compatible, it is indeed enough to provide a default value.

port: Int,
createConvertAction: @escaping () throws -> ConvertAction,
workspace: DocumentationWorkspace = DocumentationWorkspace(),
Expand All @@ -95,6 +98,7 @@ public final class PreviewAction: Action, RecreatingContext {
}

// Initialize the action context.
self.host = host
self.port = port
self.createConvertAction = createConvertAction
self.convertAction = try createConvertAction()
Expand All @@ -108,13 +112,14 @@ public final class PreviewAction: Action, RecreatingContext {
@available(*, deprecated, message: "TLS support has been removed.")
public convenience init(
tlsCertificateKey: URL?, tlsCertificateChain: URL?, serverUsername: String?,
serverPassword: String?, port: Int,
serverPassword: String?, host: String, port: Int,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This initializer is already deprecated, we should not change the signature

createConvertAction: @escaping () throws -> ConvertAction,
workspace: DocumentationWorkspace = DocumentationWorkspace(),
context: DocumentationContext? = nil,
printTemplatePath: Bool = true) throws
{
try self.init(
host: host,
port: port,
createConvertAction: createConvertAction,
workspace: workspace,
Expand Down Expand Up @@ -163,13 +168,13 @@ public final class PreviewAction: Action, RecreatingContext {
// Preview the output and monitor the source bundle for changes.
do {
print(String(repeating: "=", count: 40), to: &logHandle)
if let previewURL = URL(string: "http://localhost:\(port)") {
if let previewURL = URL(string: "http://\(host):\(port)") {
print("Starting Local Preview Server", to: &logHandle)
printPreviewAddresses(base: previewURL)
print(String(repeating: "=", count: 40), to: &logHandle)
}

let to: PreviewServer.Bind = bindServerToSocketPath.map { .socket(path: $0) } ?? .localhost(port: port)
let to: PreviewServer.Bind = bindServerToSocketPath.map { .socket(path: $0) } ?? .localhost(host: host, port: port)
servers[serverIdentifier] = try PreviewServer(contentURL: convertAction.targetDirectory, bindTo: to, logHandle: &logHandle)

// When the user stops docc - stop the preview server first before exiting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extension PreviewAction {
{
// Initialize the `PreviewAction` from the options provided by the `Preview` command
try self.init(
host: previewOptions.host,
port: previewOptions.port,
createConvertAction: {
try ConvertAction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ import Foundation
public struct PreviewOptions: ParsableArguments {
public init() { }

/// The host name to use for the preview web server.
///
/// Defaults to `localhost`.
@Option(
tomstuart marked this conversation as resolved.
Show resolved Hide resolved
name: .long,
help: ArgumentHelp(
"Host name to use for the preview web server.",
valueName: "host-name"))
public var host: String = "localhost"

/// The port number to use for the preview web server.
///
/// Defaults to `8080`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ extension Docc {
public static var configuration = CommandConfiguration(
abstract: "Convert documentation inputs and preview the documentation output.",
usage: """
docc preview [<catalog-path>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>]
docc preview [<catalog-path>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>] [--output-dir <output-dir>]
docc preview [<catalog-path>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>] [--output-dir <output-dir>] [<availability-options>] [<diagnostic-options>] [<source-repository-options>] [<hosting-options>] [<info-plist-fallbacks>] [<feature-flags>] [<other-options>]
docc preview [<catalog-path>] [--host <host-name>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>]
docc preview [<catalog-path>] [--host <host-name>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>] [--output-dir <output-dir>]
docc preview [<catalog-path>] [--host <host-name>] [--port <port-number>] [--additional-symbol-graph-dir <symbol-graph-dir>] [--output-dir <output-dir>] [<availability-options>] [<diagnostic-options>] [<source-repository-options>] [<hosting-options>] [<info-plist-fallbacks>] [<feature-flags>] [<other-options>]
""",
discussion: """
The 'preview' command extends the 'convert' command by running a preview server and monitoring the documentation input for modifications to rebuild the documentation.
Expand Down
22 changes: 11 additions & 11 deletions Sources/SwiftDocCUtilities/PreviewServer/PreviewServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ final class PreviewServer {
/// The server did not find the content directory.
case pathNotFound(String)
/// Cannot bind the server to the given address
case cannotStartServer(port: Int)
case cannotStartServer(host: String, port: Int)
/// The given port is not available
case portNotAvailable(port: Int)
case portNotAvailable(host: String, port: Int)

var errorDescription: String {
switch self {
case .failedToStart: return "Failed to start preview server"
case .pathNotFound(let path): return "The preview content path '\(path)' is not found"
case .cannotStartServer(let port): return "Can't start the preview server on port \(port)"
case .portNotAvailable(let port): return "Port \(port) is not available at the moment, "
case .cannotStartServer(let host, let port): return "Can't start the preview server on host \(host) and port \(port)"
case .portNotAvailable(let host, let port): return "Port \(port) is not available on host \(host) at the moment, "
+ "try a different port number by adding the option '--port XXXX' "
+ "to your command invocation where XXXX is the desired (free) port."
}
Expand All @@ -72,15 +72,15 @@ final class PreviewServer {
/// A list of server-bind destinations.
public enum Bind: CustomStringConvertible {
/// A port on the local machine.
case localhost(port: Int)
case localhost(host: String, port: Int)

/// A file socket on disk.
case socket(path: String)

var description: String {
switch self {
case .localhost(port: let port):
return "localhost:\(port)"
case .localhost(host: let host, port: let port):
return "\(host):\(port)"
case .socket(path: let path):
return path
}
Expand Down Expand Up @@ -146,21 +146,21 @@ final class PreviewServer {
do {
// Bind to the given destination
switch bindTo {
case .localhost(let port):
channel = try bootstrap.bind(host: "localhost", port: port).wait()
case .localhost(let host, let port):
channel = try bootstrap.bind(host: host, port: port).wait()
case .socket(let path):
channel = try bootstrap.bind(unixDomainSocketPath: path).wait()
}
} catch let error as NIO.IOError where error.errnoCode == EADDRINUSE {
// The given port is not available.
switch bindTo {
case .localhost(let port): throw Error.portNotAvailable(port: port)
case .localhost(let host, let port): throw Error.portNotAvailable(host: host, port: port)
default: throw error
}
} catch {
// Cannot bind the given address/port.
switch bindTo {
case .localhost(let port): throw Error.cannotStartServer(port: port)
case .localhost(let host, let port): throw Error.cannotStartServer(host: host, port: port)
default: throw error
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class PreviewActionIntegrationTests: XCTestCase {
// tlsCertificateChain: nil,
// serverUsername: nil,
// serverPassword: nil,
// host: "localhost",
// port: 8080, // We ignore this value when we set the `bindServerToSocketPath` property below.
// createConvertAction: createConvertAction) else {
// XCTFail("Could not create preview action from parameters")
Expand Down Expand Up @@ -268,7 +269,7 @@ class PreviewActionIntegrationTests: XCTestCase {

func testThrowsHumanFriendlyErrorWhenCannotStartServerOnAGivenPort() throws {
// Binding an invalid address
try assert(bindPort: -1, expectedErrorMessage: "Can't start the preview server on port -1")
try assert(bindPort: -1, expectedErrorMessage: "Can't start the preview server on host localhost and port -1")
}

func assert(bindPort: Int, expectedErrorMessage: String, file: StaticString = #file, line: UInt = #line) throws {
Expand Down Expand Up @@ -301,6 +302,7 @@ class PreviewActionIntegrationTests: XCTestCase {
}

guard let preview = try? PreviewAction(
host: "localhost",
port: bindPort,
createConvertAction: createConvertAction) else {
XCTFail("Could not create preview action from parameters", file: file, line: line)
Expand Down Expand Up @@ -375,6 +377,7 @@ class PreviewActionIntegrationTests: XCTestCase {
}

guard let preview = try? PreviewAction(
host: "localhost",
port: 0, // Use port 0 to pick a random free port number
createConvertAction: createConvertAction) else {
XCTFail("Could not create preview action from parameters")
Expand Down Expand Up @@ -404,7 +407,7 @@ class PreviewActionIntegrationTests: XCTestCase {
let boundPort = try XCTUnwrap(servers[preview.serverIdentifier]?.channel.localAddress?.port)

// Try to start another preview on the same port
try assert(bindPort: boundPort, expectedErrorMessage: "Port \(boundPort) is not available at the moment, try a different port number")
try assert(bindPort: boundPort, expectedErrorMessage: "Port \(boundPort) is not available on host localhost at the moment, try a different port number")

try preview.stop()

Expand Down Expand Up @@ -449,6 +452,7 @@ class PreviewActionIntegrationTests: XCTestCase {
}

guard let preview = try? PreviewAction(
host: "localhost",
port: 8080, // We ignore this value when we set the `bindServerToSocketPath` property below.
createConvertAction: createConvertAction) else {
XCTFail("Could not create preview action from parameters")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class PreviewServerTests {
}

func testPreviewServerBindDescription() {
let localhostBind = PreviewServer.Bind.localhost(port: 1234)
let localhostBind = PreviewServer.Bind.localhost(host: "localhost", port: 1234)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also test that by default, "localhost" is the host?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s the default in the sense that the --host option (in PreviewOptions) now has the value localhost by default, but PreviewServer itself has no default host — an explicit value is always required. Are you suggesting we add a PreviewOptions test to check that --host gets the right default, or something different? There’s not currently a PreviewOptionsTests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be good to test that --host gets the right default. The best place for that would be in PreviewSubcommandTests, it looks like.

XCTAssertEqual("\(localhostBind)", "localhost:1234")
let socketBind = PreviewServer.Bind.socket(path: "/tmp/file.sock")
XCTAssertEqual("\(socketBind)", "/tmp/file.sock")
Expand Down