AWS S3 Deployment with Publish
I started my journey into building this website once John Sundell released the final package in his static site generation suite of tools, Publish. The first piece of infrastructure I wanted to get set up was automated deployment. I had already configured an Amazon AWS S3 bucket to host a static website. Then I just had to figure out how to hook up a custom deployment service to Publish. It turned out to be super easy.
To configure a deployment service for Publish just create an instance of DeploymentMethod
and add it to your publishing pipeline with PublishingStep.deploy(using:)
factory method. The initializer for DeploymentMethod
is simple; it takes a name for the deployment and a closure to perform the deploy. It is recommended to create factory methods for reusable deployment methods so they can be added like this: .deploy(using: .awsS3())
.
I initially tried using Amazon's iOS and Smoke SDKs but those are large dependencies that really made building and iteration slow. I settled on an approach that involved launching a shell to use aws2 command line tools. The command I used was:
aws2 s3 sync <<source>> s3:<<bucket>>/<<key>>
I had issues getting shellOut
to find the aws2
executable so I just substituted the full path: /usr/local/bin/aws2
. I tested the command with the --dryrun
argument to ensure that I had the source and destination correct. Additionally, I wanted to use an IAM log in so I passed the --profile
argument.
I created the profile using the AWS CLI like this:
aws2 configure --profile josephvb-website
It prompts for the access key, secret, and region. After that, the profile is ready to be used.
The plugin for Publish was simple to write once I had the AWS CLI working. I could have used the /Output
folder to sync to S3 but Publish had a function createDeploymentFolder
that seemed to be more in line with the design.
import Publish
import ShellOut
extension DeploymentMethod {
static func awsS3(
bucket: String = "josephvb.com",
s3Profile: String = "josephvb-website"
) -> Self {
Self(name: "AWS-S3") { context in
let deploymentFolder = try context.createDeploymentFolder(
withPrefix: "AWS-S3-", configure: { _ in }
)
try shellOut(
to: "aws2 s3 sync . s3:\(bucket) --profile \(s3Profile)",
at: deploymentFolder.path,
outputHandle: .standardOutput,
errorHandle: .standardError
)
}
}
}
Note that I used Self
because DeploymentMethod
is generic over the website.
Overall the final result is really satisfying. I successfully added a custom deployment for Publish and in very few lines of code.