daily-notifier/deploy_plans_via_ftp.nim
2016-10-15 22:59:07 -05:00

104 lines
3.4 KiB
Nim

import json, nre, os, osproc, sequtils, sets, strutils, tempfile
proc doStep(step, cmd: string): tuple[output: TaintedString, exitCode: int] =
echo "> " & cmd
result = execCmdEx(cmd, {poUsePath})
if result.exitCode != 0:
writeLine(stderr, "Failed step [" & step &
"] Received error code: " & $result.exitCode)
quit(1)
echo ""
when isMainModule:
let cfgFilePath = $getEnv("HOME") & "/.personal-planning.config.json"
if not existsFile(cfgFilePath):
quit("Cannot find config file: " & cfgFilePath, 2)
var cfg: JsonNode
try: cfg = parseFile(cfgFilePath)
except: quit("Could not parse as json: " & cfgFilePath, 3)
let repoUrl =
if cfg.hasKey("repo"): cfg["repo"].str
else: "_git@git.jdb-labs.com:jdb/personal-planning.git"
let ftpRoot =
if cfg.hasKey("ftp") and cfg["ftp"].hasKey("root"): cfg["ftp"]["root"].str
else: "ftp://ftp.fastmail.com/jonathan.jdbernard.com/files/personal-planning"
let ftpUsername =
if cfg.hasKey("ftp") and cfg["ftp"].hasKey("username"):
cfg["ftp"]["username"].str
else: "jonathan@jdbernard.com"
if not (cfg.hasKey("ftp") and cfg["ftp"].hasKey("password")):
quit("Could not find ftp.password in config file: " & cfgFilePath, 4)
let ftpPassword = cfg["ftp"]["password"].str
let htmlFileRe =
if cfg.hasKey("outputPattern"): re(cfg["outputPattern"].str)
else: re".* (\S+)\.html?$"
let mdFileRe =
if cfg.hasKey("inputPattern"): re(cfg["inputPattern"].str)
else: re".*/(\S+-plan)\.md?$"
const subDirs = ["daily", "weekly", "monthly", "yearly"]
var output: string
var exitCode: int
# Make a temprary directory
let tempdir = mkdtemp("personal-planning-")
# Checkout personal development repository (local clone)
discard doStep("clone repo", "git clone " & repoUrl & " " & tempdir)
for curDir in subDirs:
let fullDirPath = tempdir & "/" & curDir
let remoteOptions = " --user '" &
ftpUsername & ":" & ftpPassword & "' '" &
ftpRoot & "/" & curDir & "/' "
# List the files on the server.
(output, exitCode) = doStep("read remote files (" & curDir & ")",
"curl " & remoteOptions)
let remoteFiles = output.splitLines
.filterIt(it.find(htmlFileRe).isSome)
.mapIt(it.find(htmlFileRe).get().captures[0])
.toSet
# List the daily files locally.
let localFiles =
sequtils.toSeq(walkDir(fullDirPath))
.filterIt(it.kind == pcFile and
it.path.find(mdFileRe).isSome)
.mapIt(it.path.find(mdFileRe).get().captures[0])
.toSet
# ID the files that are new (present locally but not remotely).
let newFiles = localFiles - remoteFiles
for fileName in newFiles:
let tempPath = fullDirPath & "/temp.html"
let filePath = fullDirPath & "/" & fileName
# Compile the markdown into HTML
discard doStep("compile plan file (" & fileName & ")",
"markdown " & filePath & ".md" & " > " & tempPath)
# Concatenate the HTML template to create the final HTML
discard doStep("concatenate HTML template (" & fileName & ")",
"cat " & tempdir & "/code/start.html " & tempPath & " " &
tempdir & "/code/end.html > " & filePath & ".html")
# Upload the new file to FastMail
discard doStep("upload file to FastMail (" & fileName & ")",
"curl -T '" & filePath & ".html' " & remoteOptions)
# Delete local temp repo
echo "Deleting " & tempdir
removeDir(tempdir)