-
Notifications
You must be signed in to change notification settings - Fork 166
/
main.go
236 lines (200 loc) · 7.12 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package main
import (
"fmt"
"os"
"strings"
"github.com/joeshaw/envdecode"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
"github.com/brandur/modulir"
"github.com/brandur/modulir/modules/mimage"
"github.com/brandur/sorg/modules/scommon"
)
//////////////////////////////////////////////////////////////////////////////
//
//
//
// Main
//
//
//
//////////////////////////////////////////////////////////////////////////////
func main() {
rootCmd := &cobra.Command{
Use: "sorg",
Short: "Sorg is a static site generator",
Long: strings.TrimSpace(`
Sorg is a static site generator for Brandur's personal
homepage and some of its adjacent functions. See the product
in action at https://brandur.org.`),
}
buildCommand := &cobra.Command{
Use: "build",
Short: "Run a single build loop",
Long: strings.TrimSpace(`
Starts the build loop that watches for local changes and runs
when they're detected. A webserver is started on PORT (default
5002).`),
Run: func(_ *cobra.Command, _ []string) {
modulir.Build(getModulirConfig(), build)
},
}
rootCmd.AddCommand(buildCommand)
loopCommand := &cobra.Command{
Use: "loop",
Short: "Start build and serve loop",
Long: strings.TrimSpace(`
Runs the build loop one time and places the result in TARGET_DIR
(default ./public/).`),
Run: func(_ *cobra.Command, _ []string) {
modulir.BuildLoop(getModulirConfig(), build)
},
}
rootCmd.AddCommand(loopCommand)
var live bool
var staging bool
sendCommand := &cobra.Command{
Use: "send [source newsletter .md file]",
Short: "Email a Nanoglyph or Passages newsletter",
Long: strings.TrimSpace(`
Emails the Nanoglyph or Passages newsletter at the location given
as argument. Note that MAILGUN_API_KEY must be set in the
environment for this to work as it executes against the Mailgun
API.`),
Args: cobra.ExactArgs(1),
Run: func(_ *cobra.Command, args []string) {
c := &modulir.Context{Log: getLog()}
sendNewsletter(c, args[0], live, staging)
},
}
sendCommand.Flags().BoolVar(&live, "live", false,
"Send to list (as opposed to dry run)")
sendCommand.Flags().BoolVar(&staging, "staging", false,
"Send to staging list (as opposed to dry run)")
rootCmd.AddCommand(sendCommand)
if err := envdecode.Decode(&conf); err != nil {
fmt.Fprintf(os.Stderr, "Error decoding conf from env: %v", err)
os.Exit(1)
}
mimage.MagickBin = conf.MagickBin
mimage.MozJPEGBin = conf.MozJPEGBin
mimage.PNGQuantBin = conf.PNGQuantBin
mimage.TempDir = scommon.TempDir
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error executing command: %v", err)
os.Exit(1)
}
}
//////////////////////////////////////////////////////////////////////////////
//
//
//
// Variables
//
//
//
//////////////////////////////////////////////////////////////////////////////
// Left as a global for now for the sake of convenience, but it's not used in
// very many places and can probably be refactored as a local if desired.
var conf Conf
//////////////////////////////////////////////////////////////////////////////
//
//
//
// Types
//
//
//
//////////////////////////////////////////////////////////////////////////////
// Conf contains configuration information for the command. It's extracted from
// environment variables.
type Conf struct {
// AbsoluteURL is the absolute URL where the compiled site will be hosted.
// It's used for things like Atom feeds and sending email.
AbsoluteURL string `env:"ABSOLUTE_URL,default=https://brandur.org"`
// BlackSwanDatabaseURL is a connection string for a database to connect to
// in order to extract books, tweets, runs, etc.
BlackSwanDatabaseURL string `env:"BLACK_SWAN_DATABASE_URL"`
// Concurrency is the number of build Goroutines that will be used to
// perform build work items.
Concurrency int `env:"CONCURRENCY,default=30"`
// Drafts is whether drafts of articles and fragments should be compiled
// along with their published versions.
//
// Activating drafts also prompts the creation of a robots.txt to make sure
// that drafts aren't inadvertently accessed by web crawlers.
Drafts bool `env:"DRAFTS,default=false"`
// EnableGoatCounter enables Goat Counter analytics, which is a less
// invasive version of Google Analytics that collects less information,
// does less user tracking, and is not an ad company. Currently I'm
// experimenting with it as a possibility of a full alternative.
EnableGoatCounter bool `env:"ENABLE_GOAT_COUNTER,default=false"`
// GoogleAnalyticsID is the account identifier for Google Analytics to use.
GoogleAnalyticsID string `env:"GOOGLE_ANALYTICS_ID"`
// LocalFonts starts using locally downloaded versions of Google Fonts.
// This is not ideal for real deployment because you won't be able to
// leverage Google's CDN and the caching that goes with it, and may not get
// the font format for requesting browsers, but good for airplane rides
// where you otherwise wouldn't have the fonts.
LocalFonts bool `env:"LOCAL_FONTS,default=false"`
// MailgunAPIKey is a key for Mailgun used to send email. It's required
// when using the `passages` command.
MailgunAPIKey string `env:"MAILGUN_API_KEY"`
// MagickBin is the location of the `magick` binary that ships with the
// ImageMagick project (an image manipulation utility).
MagickBin string `env:"MAGICK_BIN"`
// MozJPEGBin is the location of the `cjpeg` binary that ships with the
// mozjpeg project (a JPG optimizer). If configured, Sorg will put photos
// through an optimization pass after resizing them.
MozJPEGBin string `env:"MOZJPEG_BIN"`
// NumAtomEntries is the number of entries to put in Atom feeds.
NumAtomEntries int `env:"NUM_ATOM_ENTRIES,default=20"`
// PNGQuantBin is the location of the `pnqquant` binary (a PNG optimizer). If
// configured, PNGs are passed through an optimization pass after resizing
// them.
PNGQuantBin string `env:"PNGQUANT_BIN"`
// Port is the port on which to serve HTTP when looping in development.
Port int `env:"PORT,default=5002"`
// SorgEnv is the environment to run the app with. Use "development" to
// activate development features.
SorgEnv string `env:"SORG_ENV,default=production"`
// TargetDir is the target location where the site will be built to.
TargetDir string `env:"TARGET_DIR,default=./public"`
// Verbose is whether the program will print debug output as it's running.
Verbose bool `env:"VERBOSE,default=false"`
}
//////////////////////////////////////////////////////////////////////////////
//
//
//
// Private
//
//
//
//////////////////////////////////////////////////////////////////////////////
const (
sorgEnvDevelopment = "development"
)
func getLog() *logrus.Logger {
log := logrus.New()
if conf.Verbose {
log.SetLevel(logrus.DebugLevel)
} else {
log.SetLevel(logrus.InfoLevel)
}
return log
}
// getModulirConfig interprets Conf to produce a configuration suitable to pass
// to a Modulir build loop.
func getModulirConfig() *modulir.Config {
return &modulir.Config{
Concurrency: conf.Concurrency,
Log: getLog(),
LogColor: term.IsTerminal(int(os.Stdout.Fd())),
Port: conf.Port,
SourceDir: ".",
TargetDir: conf.TargetDir,
Websocket: conf.SorgEnv == sorgEnvDevelopment,
}
}