Skip to content

Commit 7d0b1e7

Browse files
committed
add a tool for automatically configuring MongoDB, and update Quickstart guide
1 parent 91c69ce commit 7d0b1e7

File tree

8 files changed

+216
-24
lines changed

8 files changed

+216
-24
lines changed

Guides/docs.docc/Quickstart.md

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,42 +41,26 @@ Unidoc is an ordinary SwiftPM executable product. You can build it for your macO
4141

4242
@Code(file: unidoc-from-source.sh, title: unidoc-from-source.sh)
4343

44-
Please note that the executable as built by SwiftPM is named `unidoc-tools`, which you should rename to `unidoc` when installing it.
45-
4644

4745
> Important:
4846
> The rest of this guide assumes you have installed Unidoc somewhere on your macOS host that is visible in your `PATH` and allows you to invoke it as `unidoc`.
4947
5048

5149
## 3. Launching a `mongod` instance
5250

53-
If you have not already done so, clone the Unidoc project and navigate to the root of the repository:
51+
Unidoc can configure a `mongod` instance for you through the `unidoc init` command. This tool takes a directory path as an argument, which it uses to persist the state of the database. In the example below, we will create the documentation database in a directory named `unidoc` in your home directory.
5452

5553
```bash
56-
git clone https://github.com/tayloraswift/swift-unidoc
57-
cd swift-unidoc
54+
unidoc init ~/unidoc
5855
```
5956

60-
Use Docker Compose to launch a `mongod` instance in a container. This container is named `unidoc-mongod-container`. It has persistent state which `mongod` stores in a directory called `.mongod` at the root of the repository.
61-
62-
```bash
63-
docker compose -f Guides/docs.docc/local/docker-compose.yml up
64-
```
57+
Please note that this will start a Docker container that runs continuously in the background. Therefore, if you want to dismantle the database, you must stop the container before deleting the persistence directory, otherwise it may recreate some of the files you delete.
6558

6659
@Image(source: "Docker Desktop.png", alt: "Docker Desktop") {
6760
> You should see the `unidoc-mongod-container` running in the Docker Desktop GUI.
6861
}
6962

7063

71-
The container is home to a MongoDB [replica set](https://www.mongodb.com/docs/manual/reference/replica-configuration/) which you need to initialize.
72-
73-
Open a new terminal and run the following command to initialize the replica set:
74-
75-
```bash
76-
docker exec -t unidoc-mongod-container /bin/mongosh --file /unidoc-rs-init.js
77-
```
78-
79-
8064
## 3. Running `unidoc preview`
8165

8266
The `unidoc preview` tool is a simple web server that links and serves documentation for local Swift packages. Run it directly from your macOS host like this:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
swift run -c release unidoc-tools local swift
1+
swift run -c release unidoc local swift
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
swift run -c release unidoc-tools local swift-nio -I /swift
1+
swift run -c release unidoc local swift-nio -I /swift
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
swift --version
2-
swift run -c release unidoc-tools preview
2+
swift run -c release unidoc preview
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mkdir -p ~/unidoc/bin
22
git clone https://github.com/tayloraswift/swift-unidoc
33
cd swift-unidoc
4-
swift build -c release --product unidoc-tools
5-
mv .build/release/unidoc-tools ~/unidoc/bin/unidoc
4+
swift build -c release --product unidoc
5+
mv .build/release/unidoc ~/unidoc/bin/unidoc
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import System
2+
3+
extension Main.Init
4+
{
5+
struct Installation
6+
{
7+
let docker_compose_yml:FilePath
8+
let unidoc_rs_init_js:FilePath
9+
let unidoc_rs_conf:FilePath
10+
11+
let container:String
12+
let localhost:Bool
13+
}
14+
}
15+
extension Main.Init.Installation
16+
{
17+
func create() throws
18+
{
19+
for (file, content):(FilePath, String) in [
20+
(
21+
self.docker_compose_yml,
22+
Self.docker_compose_yml(container: self.container)
23+
),
24+
(
25+
self.unidoc_rs_conf,
26+
Self.unidoc_rs_conf
27+
),
28+
(
29+
self.unidoc_rs_init_js,
30+
Self.unidoc_rs_init_js(host: self.localhost ? "localhost" : "unidoc-mongod")
31+
)
32+
]
33+
{
34+
try file.open(.writeOnly, permissions: (.rw, .r, .r), options: [.create, .truncate])
35+
{
36+
_ = try $0.writeAll(content.utf8)
37+
}
38+
}
39+
}
40+
}
41+
42+
extension Main.Init.Installation
43+
{
44+
private
45+
static func docker_compose_yml(container:String) -> String
46+
{
47+
"""
48+
services:
49+
unidoc-mongod:
50+
image: mongo:latest
51+
container_name: \(container)
52+
hostname: unidoc-mongod
53+
networks:
54+
- unidoc-test
55+
ports:
56+
- 27017:27017
57+
restart: always
58+
volumes:
59+
# cannot put this in docker-entrypoint-initdb.d, it does not work
60+
- ./unidoc-rs-init.js:/unidoc-rs-init.js
61+
- ./unidoc-rs.conf:/etc/mongod.conf
62+
- ./mongod:/data/db
63+
command: mongod --config /etc/mongod.conf
64+
65+
66+
networks:
67+
unidoc-test:
68+
name: unidoc-test
69+
enable_ipv6: true
70+
ipam:
71+
config:
72+
- subnet: 2001:0DB8::/112
73+
74+
"""
75+
}
76+
77+
private
78+
static let unidoc_rs_conf:String = """
79+
replication:
80+
replSetName: unidoc-rs
81+
net:
82+
port: 27017
83+
bindIp: 0.0.0.0
84+
bindIpAll: true
85+
86+
"""
87+
88+
private
89+
static func unidoc_rs_init_js(host:String) -> String
90+
{
91+
"""
92+
db = connect('mongodb://unidoc-mongod:27017/admin');
93+
db.runCommand({'replSetInitiate': {
94+
"_id": "unidoc-rs",
95+
"version": 1,
96+
"members": [
97+
{
98+
"_id": 0,
99+
"host": "\(host):27017",
100+
"tags": {},
101+
"priority": 1
102+
}
103+
]
104+
}});
105+
106+
"""
107+
}
108+
}

Sources/unidoc-tools/Main.Init.swift

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import ArgumentParser
2+
import System
3+
4+
extension Main
5+
{
6+
struct Init
7+
{
8+
@Argument
9+
var location:FilePath.Directory
10+
11+
@Option(
12+
name: [.customLong("container"), .customShort("c")],
13+
help: "Container name")
14+
var container:String = "unidoc-mongod-container"
15+
16+
@Flag(
17+
name: [.customLong("containerized"), .customShort("m")],
18+
help: """
19+
Use containerized setup - this prevents documentation preview servers from running \
20+
on the host, but allows preview servers to run from inside other Docker containers
21+
""")
22+
var containerized:Bool = false
23+
}
24+
}
25+
extension Main.Init:AsyncParsableCommand
26+
{
27+
public
28+
static let configuration:CommandConfiguration = .init(commandName: "init")
29+
30+
func run() async throws
31+
{
32+
try self.location.create()
33+
34+
let installation:Installation = .init(
35+
docker_compose_yml: self.location / "docker-compose.yml",
36+
unidoc_rs_init_js: self.location / "unidoc-rs-init.js",
37+
unidoc_rs_conf: self.location / "unidoc-rs.conf",
38+
container: self.container,
39+
localhost: !self.containerized)
40+
41+
try installation.create()
42+
43+
try SystemProcess.init(command: "docker",
44+
"compose",
45+
"--file", "\(installation.docker_compose_yml)",
46+
"up",
47+
"--detach",
48+
"--wait",
49+
echo: true)()
50+
51+
// Even though the container is ready, the `mongod` daemon within it may not be.
52+
// We need to wait for it to be ready before we can run the `mongosh` command.
53+
// We do this by pinging the `mongod` daemon until it responds.
54+
print("Waiting for mongod to start up...")
55+
56+
var attempts:Int = 0
57+
waiting: do
58+
{
59+
async
60+
let interval:Void = Task.sleep(for: .seconds(1))
61+
do
62+
{
63+
try SystemProcess.init(command: "docker",
64+
"exec",
65+
"\(installation.container)",
66+
"mongosh",
67+
"--quiet",
68+
"--eval", "exit")()
69+
}
70+
catch let error
71+
{
72+
if attempts > 10
73+
{
74+
throw error
75+
}
76+
else
77+
{
78+
attempts += 1
79+
}
80+
81+
try await interval
82+
continue waiting
83+
}
84+
}
85+
86+
print("Initializing replica set...")
87+
88+
try SystemProcess.init(command: "docker",
89+
"exec",
90+
"--tty",
91+
"\(installation.container)",
92+
"/bin/mongosh", "--file", "/unidoc-rs-init.js",
93+
echo: true)()
94+
95+
print("Successfully initialized MongoDB replica set!")
96+
print(" Docker compose file: \(installation.docker_compose_yml)")
97+
print(" Docker container: \(installation.container)")
98+
}
99+
}

Sources/unidoc-tools/Main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct Main:AsyncParsableCommand
66
{
77
static let configuration:CommandConfiguration = .init(subcommands: [
88
SSGC.Compile.self,
9+
Init.self,
910
Local.self,
1011
Preview.self
1112
])

0 commit comments

Comments
 (0)