W.D. Neumann's Thing

First you ploy, then you deploy

So Publish comes with a built-in deployment method for uploading your generated website to GitHub, which is nice for those who use GitHub for hosting their sites. But for those of us who use other hosting, it's not so useful.
Fortunately, adding your own custom deployment methods is pretty straightforward. If we look in DeploymentMethod.swift, we see that a deployment method is just a simple struct with a string that holds the name of the deployment method (to print in Publish's terminal output) and a body which is just a closure that takes the site's publishing context and contains the instructions used to deploy the site:

public struct DeploymentMethod<Site: Website> {
    /// Closure type used to implement the deployment method's main
    /// body. It's passed the `PublishingContext` of the current
    /// session, and can use that to create a dedicated deployment folder.
    public typealias Body = (PublishingContext<Site>) throws -> Void

    /// The human-readable name of the deployment method.
    public var name: String
    /// The deployment method's main body. See `Body` for more info.
    public var body: Body

    /// Initialize a new deployment method.
    /// - parameter name: The method's human-readable name.
    /// - parameter body: The method's main body.
    public init(name: String, body: @escaping Body) {
        self.name = name
        self.body = body
    }
}

One can then, similar to adding a custom processing step, add a static method that provides a DeploymentMethod that does the work we need:

extension DeploymentMethod where Site == Wdneumann {
  static func scp(user: String, host: String, directory: String) -> Self {
    DeploymentMethod(name: "scp to (\(host))") { context in
      let folder = try context.createDeploymentFolder(withPrefix: "") { _ in () }
      try shellOut(
        to: "scp",
        arguments: ["-r", "-i", "~/.ssh/id_rsa.pub", "\(folder.path)*", "\(user)@\(host):\(directory)"],
        at: folder.path
      )
    }
  }
}

This is pretty self-explanatory. I take the folder that the generated site is copied to, and I use scp to upload it to the specified folder on the specified host, with the specified username. I use pre-shared ssh keys for authentication here.

The only bit that really requires any explanation here is the { _ in () } bit. That is a closure that is passed into context.createDeploymentFolder that allows you to do a bit of pre-work inside the created folder before the contents are copied into it. The GitHub method uses this to make sure a git repository is set up there and also sets up the remote repository information. I have nothing to do here, so I pass in a closure that does nothing. I'm a little surprised that's not an optional closure with a default value of nil, but that's neither here nor there.

Now, the deployment method can be plugged into the publishing pipeline in main.swift:

   .publish(
    withTheme: .yourTheme,
    deployedUsing: .scp(user: "username", host: "your.site.host", directory: "baseDir/for/site"),
    additionalSteps: [.fixSectionTitles()], plugins: [.splash(withClassPrefix: "")]
  )

Now you can deploy your site by issuing a publish deploy at the command line and your site winds up where it needs to be. Easy-peasy.

Tagged with: