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)