176 lines
5.9 KiB
Markdown
176 lines
5.9 KiB
Markdown
# Setup a publishing environment
|
|
|
|
This tutorial explains how to set up a basic publishing environment using Swift. It uses the Swift Package Manager (SPM) and the library [Publish](https://github.com/johnsundell/publish) to achieve that goal. The whole process avoids using Xcode and I restrict the tooling to the command line. It uses my personal site `felixfoertsch.de` as the example project.
|
|
|
|
This tutorial is for people who want to understand the process, the tools, and the file structure involved. It shows the manual steps that the Publish CLI tool can do out-of-the-box. You can install it via [Homebrew](https://brew.sh) to create a new project and set up the environment: `brew install publish`.
|
|
|
|
## Set up the project structure
|
|
|
|
The first step to create a Swift project is to use the SPM to initialize the folder. By default, the SPM creates projects of type `library`. Since Publish requires a Swift project of type `executeable`, we have to tell that to SPM. Additionally we pass in a name for the project, in this case `felixfoertsch.de`.
|
|
|
|
```bash
|
|
mkdir ~/Developer/felixfoertsch.de
|
|
cd ~/Developer/felixfoertsch.de
|
|
swift package init --type executeable --name "felixfoertsch.de"
|
|
```
|
|
|
|
The SPM creates the default folder structure for an executable in the current directory. It looks like this:
|
|
|
|
```bash
|
|
📁 Sources
|
|
📄 .gitignore
|
|
📄 Package.swift
|
|
```
|
|
|
|
Side note: Because it is of type `executeable`, the SPM **skips** creating a `Tests` folder.
|
|
|
|
## Add the required configuration to the `Package.swift` file
|
|
|
|
The main configuration file of a Swift project is called `Package.swift`. Its home is the root folder and it contains the project's metadata, such as its name, dependencies, and targets.
|
|
|
|
The default file is pretty empty. I removed the default comments for better legibility. It looks like this:
|
|
|
|
```swift
|
|
// swift-tools-version:5.10
|
|
import PackageDescription
|
|
|
|
let package = Package(
|
|
name: "felixfoertsch.de",
|
|
targets: [
|
|
.executableTarget(
|
|
name: "felixfoertsch.de"),
|
|
]
|
|
)
|
|
```
|
|
|
|
At the time of writing, the current Swift tools are version 5.10, which is set in the first line of the `Package.swift` file.
|
|
|
|
You can see, that we are instantiating a `Package` object. We are injecting the dependencies -- or rather we are defining them there and during the build process the package takes the configuration into account. It is therefore a list of parameters we are passing to the `Package` object, separated by commas `,`. The order also matters.
|
|
|
|
### Minimum platform version
|
|
|
|
Because Publish comes with a minimum platform version, we have to set it. This goes on the top level.
|
|
|
|
```swift
|
|
platforms: [.macOS(.v12)]
|
|
```
|
|
|
|
### Product: Executable
|
|
|
|
Even though the product of our publishing project is going to be a static website, within the system we are building an executable Package.
|
|
|
|
```swift
|
|
products: [
|
|
.executable(
|
|
name: "felixfoertsch.de",
|
|
targets: ["felixfoertsch.de"]
|
|
)
|
|
],
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
The next entry is the list of dependencies. We have only one dependency at the moment: we add the `Publish` library to the `Package.swift` in the right place on the top level.
|
|
|
|
```swift
|
|
dependencies: [
|
|
.package(url: "https://github.com/johnsundell/publish.git", from: "0.9.0")
|
|
],
|
|
```
|
|
|
|
Since this is also a dependency of our executable target, we have to tell that to the compiler. We are referencing by the package name -- within the target array and for our executable target.
|
|
|
|
```swift
|
|
dependencies: [.product(name: "Publish", package: "publish")]
|
|
```
|
|
|
|
### The complete `Package.swift` file
|
|
|
|
After adding the dependencies, the complete file should look like this:
|
|
|
|
```swift
|
|
// swift-tools-version:5.10
|
|
|
|
import PackageDescription
|
|
|
|
let package = Package(
|
|
name: "felixfoertsch.de",
|
|
platforms: [.macOS(.v12)],
|
|
products: [
|
|
.executable(
|
|
name: "felixfoertsch.de",
|
|
targets: ["felixfoertsch.de"]
|
|
)
|
|
],
|
|
dependencies: [
|
|
.package(url: "https://github.com/johnsundell/publish.git", from: "0.9.0")
|
|
],
|
|
targets: [
|
|
.executableTarget(
|
|
name: "felixfoertsch.de",
|
|
dependencies: [.product(name: "Publish", package: "publish")]
|
|
)
|
|
]
|
|
)
|
|
```
|
|
|
|
This concludes the minimal required configuration. The next step is to add the website structure in Swift and its content in Markdown.
|
|
|
|
## Create the Hello, World website
|
|
|
|
The website structure written in Swift goes into the `Sources` and Markdown content in the `Content` folder. We are creating dummy files in both folders to get started.
|
|
|
|
Create a `index.swift` file in the `Sources` folder and a `index.md` in the `Content` folder. The file structure should now look like this:
|
|
|
|
```bash
|
|
📁 Content
|
|
📄 index.md
|
|
📁 Sources
|
|
📄 index.swift
|
|
📄 .gitignore
|
|
📄 Package.swift
|
|
```
|
|
|
|
Add `# Hello, World!` to the `index.md` file.
|
|
|
|
The `index.swift` file is the entry point of the website. Note that we are using an additional dependency called `Plot` that is required and automatically resolved by the `Publish` library. It is a library that allows writing HTML in Swift and is used to generate the output.
|
|
|
|
Add the following code to the `index.swift` file.
|
|
|
|
```swift
|
|
import Foundation
|
|
import Publish
|
|
import Plot
|
|
|
|
struct Index: Website {
|
|
|
|
enum SectionID: String, WebsiteSectionID {
|
|
case posts
|
|
}
|
|
|
|
struct ItemMetadata: WebsiteItemMetadata {
|
|
}
|
|
|
|
// Update these properties to configure your website:
|
|
var url = URL(string: "https://felixfoertsch.de")!
|
|
var name = "felixfoertsch.de"
|
|
var description = "A description of felixfoertsch.de"
|
|
var language: Language { .english }
|
|
var imagePath: Path? { nil }
|
|
}
|
|
|
|
// This will generate your website using the built-in Foundation theme:
|
|
try Index().publish(withTheme: .foundation)
|
|
```
|
|
|
|
```swift
|
|
swift package resolve
|
|
swift build
|
|
swift run
|
|
```
|
|
|
|
## Glossary
|
|
|
|
- **Products** define the executables and libraries a package produces, making them visible to other packages.
|
|
- **Targets** are the basic building blocks of a package, defining a module or a test suite. Targets can depend on other targets in this package and products from dependencies.
|