diff --git a/src/main/nim/strawboss.nim b/src/main/nim/strawboss.nim index f78bcf8..69a99b4 100644 --- a/src/main/nim/strawboss.nim +++ b/src/main/nim/strawboss.nim @@ -1,302 +1,23 @@ -import asyncdispatch, asyncnet, docopt, jester, json, logging, nre, os, - osproc, sequtils, streams, strtabs, strutils, tables, tempfile +import docopt, logging, os, sequtils -settings: - port = Port(8180) +import strawboss/private/util +import strawboss/configuration +import strawboss/core +import strawboss/server let SB_VER = "0.1.0" -# Types -# -type - Step* = object - name*, stepCmd*, workingDir*: string - artifacts*, cmdInput*, depends*, expectedEnv*: seq[string] - dontCache*: bool - - ProjectCfg* = object - name*: string - versionCmd*: string - steps*: Table[string, Step] - - ProjectDef* = object - cfgFilePath*, defaultBranch*, name*, repo*: string - envVars*: StringTableRef - - StrawBossCfg* = object - artifactsRepo*: string - projects*: seq[ProjectDef] - - Workspace* = ref object - artifactsRepo*: string - env*: StringTableRef - workingDir*: string - project: ProjectCfg - - HandleProcMsgCB = proc (cmd: string, outMsg: TaintedString, errMsg: TaintedString): void - -# Misc. helpers - -proc raiseEx(reason: string): void = - raise newException(Exception, reason) - -let nullNode = newJNull() -proc getIfExists(n: JsonNode, key: string): JsonNode = - result = if n.hasKey(key): n[key] - else: nullNode - - -proc loadEnv(): StringTableRef = - result = newStringTable() - - for k, v in envPairs(): - result[k] = v - -proc waitForWithOutput(p: Process, msgCB: HandleProcMsgCB, - procCmd: string = ""): - tuple[output: TaintedString, error: TaintedString, exitCode: int] = - - var pout = outputStream(p) - var perr = errorStream(p) - - result = (TaintedString"", TaintedString"", -1) - var line = newStringOfCap(120).TaintedString - while true: - if pout.readLine(line): - if msgCB != nil: msgCB(procCmd, line, nil) - result[0].string.add(line.string) - result[0].string.add("\n") - elif perr.readLine(line): - if msgCB != nil: msgCB(procCmd, nil, line) - result[1].string.add(line.string) - result[1].string.add("\n") - else: - result[2] = peekExitCode(p) - if result[2] != -1: break - close(p) - -proc exec(command: string, workingDir: string = "", - args: openArray[string] = [], env: StringTableRef = nil, - options: set[ProcessOption] = {poUsePath}, - msgCB: HandleProcMsgCB = nil): - tuple[output: TaintedString, error: TaintedString, exitCode: int] - {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = - - var p = startProcess(command, workingDir, args, env, options) - result = waitForWithOutput(p, msgCb, command) - -let ENV = loadEnv() -let logProcOutput: HandleProcMsgCB = proc (cmd: string, outMsg: TaintedString, errMsg: TaintedString) = - if outMsg != nil: info cmd & "(stdout): " & outMsg - if errMsg != nil: info cmd & "(stderr): " & errMsg - -let envVarRe = re"\$\w+|\$\{[^}]+\}" -proc resolveEnvVars(line: string, env: StringTableRef): string = - result = line - for found in line.findAll(envVarRe): - let key = if found[1] == '{': found[2..^2] else: found[1..^1] - if env.hasKey(key): result = result.replace(found, env[key]) - -let SB_EXPECTED_VARS = ["VERSION"] -# Configuration parsing code - -proc loadStrawBossConfig(cfgFile: string): StrawBossCfg = - let jsonCfg = parseFile(cfgFile) - - var projectDefs: seq[ProjectDef] = @[] - - for pJson in jsonCfg.getIfExists("projects").getElems: - if not pJson.hasKey("name"): - raiseEx "a project definition is missing the project name" - - if not pJson.hasKey("repo"): - raiseEx "a project definition is missing the project repo configuration" - - var envVars = newStringTable(modeCaseSensitive) - for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("") - - projectDefs.add( - ProjectDef( - cfgFilePath: pJson.getIfExists("cfgFilePath").getStr("strawboss.json"), - defaultBranch: pJson.getIfExists("defaultBranch").getStr("master"), - name: pJson["name"].getStr, - envVars: envVars, - repo: pJson["repo"].getStr)) - - result = StrawBossCfg( - artifactsRepo: jsonCfg.getIfExists("artifactsRepo").getStr("artifacts"), - projects: projectDefs) - -proc loadProjectConfig(cfgFile: string): ProjectCfg = - let jsonCfg = parseFile(cfgFile) - - if not jsonCfg.hasKey("name"): - raise newException(Exception, "project configuration is missing a name") - - if not jsonCfg.hasKey("steps"): - raise newException(Exception, "project configuration is missing steps definition") - - var steps = initTable[string, Step]() - for sName, pJson in jsonCfg["steps"].getFields: - steps[sName] = Step( - name: sName, - workingDir: pJson.getIfExists("workingDir").getStr("."), - stepCmd: pJson.getIfExists("stepCmd").getStr("sh"), - depends: pJson.getIfExists("depends").getElems.mapIt(it.getStr), - artifacts: pJson.getIfExists("artifacts").getElems.mapIt(it.getStr), - cmdInput: pJson.getIfExists("cmdInput").getElems.mapIt(it.getStr), - expectedEnv: pJson.getIfExists("expectedEnv").getElems.mapIt(it.getStr), - dontCache: pJson.getIfExists("dontCache").getStr("false") != "false") - - if steps[sName].stepCmd == "sh" and steps[sName].cmdInput.len == 0: - warn "Step " & sName & " uses 'sh' as its command but has no cmdInput." - - result = ProjectCfg( - name: jsonCfg["name"].getStr, - versionCmd: jsonCfg.getIfExists("versionCmd").getStr("git describe --tags --always"), - steps: steps) - -proc setupProjectForWork(projectDef: ProjectDef, buildRef, artifactsRepo: string): Workspace = - - info "Setting up to do work for '" & projectDef.name & "' at ref " & buildRef & "." - var env = ENV - env["GIT_DIR"] = ".git" - - # Create a temp directory that we'll work in - let projDir = mkdtemp() - debug "Workspace for '" & projectDef.name & ": " & projDir - assert projDir.isAbsolute - - # Clone the project into the $temp/repo directory - let cloneResult = exec("git", projDir, ["clone", projectDef.repo, "repo"], - env, {poUsePath}, logProcOutput) - - if cloneResult.exitCode != 0: - removeDir(projDir) - raiseEx "unable to clone repo for '" & projectDef.name & "'" - - # Checkout the requested ref - let checkoutResult = exec("git", projDir & "/repo", ["checkout", buildRef], - env, {poUsePath}, logProcOutput) - - if checkoutResult.exitCode != 0: - removeDir(projDir) - raiseEx "unable to checkout ref " & buildRef & " for '" & projectDef.name & "'" - - # Find the strawboss project configuration - let projCfgFile = projDir & "/repo/" & projectDef.cfgFilePath - if not existsFile(projCfgFile): - removeDir(projDir) - raiseEx "Cannot find strawboss project configuration in the project " & - "repo (expected at '" & projectDef.cfgFilePath & "')." - - let projectCfg = loadProjectConfig(projCfgFile) - result = Workspace(env: env, workingDir: projDir, project: projectCfg, - artifactsRepo: artifactsRepo) - - # Merge in the project-defined env vars - for k, v in projectDef.envVars: result.env[k] = v - - # Get the build version - let versionProc = startProcess( - projectCfg.versionCmd, # command - projDir & "/repo", # working dir - [], # args - result.env, # environment - {poUsePath, poEvalCommand}) # options - - let versionResult = waitForWithOutput(versionProc, logProcOutput, - projectCfg.versionCmd) - - if versionResult.exitCode != 0: - removeDir(projDir) - raiseEx "Version command (" & projectCfg.versionCmd & ") returned non-zero exit code." - - debug "Building version " & versionResult.output.strip - result.env["VERSION"] = versionResult.output.strip - -## TODO -proc runStep(step: Step, wksp: Workspace): void = - - let stepArtifactDir = wksp.artifactsRepo & "/" & wksp.project.name & "/" & step.name & "/" & wksp.env["VERSION"] - - if existsDir(stepArtifactDir) and not step.dontCache: - info "Skipping step '" & step.name & "': already completed." - return - - info "Running step '" & step.name & "' for " & wksp.project.name - - # Ensure all expected environment variables are present. - for k in (step.expectedEnv & @SB_EXPECTED_VARS): - if not wksp.env.hasKey(k): - debug "workspace.env = " & $(wksp.env) - raiseEx "step " & step.name & " failed: missing required env variable: " & k - - # Ensure that artifacts in steps we depend on are present - for dep in step.depends: - if not wksp.project.steps.hasKey(dep): - raiseEx step.name & " depends on " & dep & " but there is no step named " & dep - let depStep = wksp.project.steps[dep] - - # Run that step (may get skipped) - let depDir = wksp.artifactsRepo & "/" & wksp.project.name & "/" & dep & "/" & wksp.env["VERSION"] - runStep(depStep, wksp) - - # Add the artifacts directory for the dependent step to our env so that - # further steps can reference it via $_DIR - echo "FP: " & depDir - wksp.env[depStep.name & "_DIR"] = depDir - - # Run the step command, piping in cmdInput - debug step.name & ": starting stepCmd: " & step.stepCmd - let cmdProc = startProcess(step.stepCmd, wksp.workingDir & "/repo/" & step.workingDir, - [], wksp.env, {poUsePath, poEvalCommand}) - - let cmdInStream = inputStream(cmdProc) - - # Replace env variables in step cmdInput as we pipe it in - for line in step.cmdInput: cmdInStream.writeLine(line.resolveEnvVars(wksp.env)) - cmdInStream.flush() - cmdInStream.close() - - let cmdResult = waitForWithOutput(cmdProc, logProcOutput, step.stepCmd) - - if cmdResult.exitCode != 0: - raiseEx "step " & step.name & " failed: step command returned non-zero exit code" - - # Gather the output artifacts (if we have any) - if not existsDir(stepArtifactDir): createDir(stepArtifactDir) - if step.artifacts.len > 0: - for a in step.artifacts: - let artifactPath = a.resolveEnvVars(wksp.env) - let artifactName = artifactPath[(artifactPath.rfind("/")+1)..^1] - try: - copyFile(wksp.workingDir & "/repo/" & step.workingDir & "/" & artifactPath, - stepArtifactDir & "/" & artifactName) - except: - removeDir(stepArtifactDir) - raiseEx "step " & step.name & " failed: unable to copy artifact " & - artifactPath & ":\n" & getCurrentExceptionMsg() - -# Server routes -routes: - get "/api/ping": - resp $(%*"pong"), "application/json" - - get "/api/projects": - resp $(%*[]), "application/json" - when isMainModule: if logging.getHandlers().len == 0: logging.addHandler(newConsoleLogger()) - let cfg = loadStrawBossConfig("strawboss.config.json") + var cfg = loadStrawBossConfig("strawboss.config.json") if not existsDir(cfg.artifactsRepo): info "Artifacts repo (" & cfg.artifactsRepo & ") does not exist. Creating..." createDir(cfg.artifactsRepo) - let artifactsRepo = expandFilename(cfg.artifactsRepo) + cfg.artifactsRepo = expandFilename(cfg.artifactsRepo) let doc = """ Usage: @@ -307,31 +28,13 @@ Usage: if args["run"]: - # Find the project let projName = $args[""] - let matching = cfg.projects.filterIt(it.name == projName) - if matching.len == 0: - fatal "strawboss: no such project: " & projName - quit(QuitFailure) - elif matching.len > 1: - fatal "strawboss: more than one project named : " & projName - quit(QuitFailure) + let stepName = $args[""] + let buildRef = if args["-r"]: $args[""] else: nil - let projectDef = matching[0] - let buildRef = if args["-r"]: $args[""] else: projectDef.defaultBranch - - try: - let wksp = setupProjectForWork(projectDef, buildRef, artifactsRepo) - - # Find the step - let stepName = $args[""] - if not wksp.project.steps.hasKey(stepName): - raiseEx "no step name '" & stepName & "' for " & projName - - let step = wksp.project.steps[stepName] - runStep(step, wksp) + try: core.runStep(cfg, projName, stepName, buildRef) except: fatal "strawboss: " & getCurrentExceptionMsg() quit(QuitFailure) - elif args["serve"]: - runForever() + + elif args["serve"]: server.start(cfg) diff --git a/src/main/nim/strawboss/configuration.nim b/src/main/nim/strawboss/configuration.nim new file mode 100644 index 0000000..d27d74a --- /dev/null +++ b/src/main/nim/strawboss/configuration.nim @@ -0,0 +1,90 @@ +import logging, json, nre, sequtils, strtabs, tables +import private/util + +# Types +# +type + Step* = object + name*, stepCmd*, workingDir*: string + artifacts*, cmdInput*, depends*, expectedEnv*: seq[string] + dontCache*: bool + + ProjectCfg* = object + name*: string + versionCmd*: string + steps*: Table[string, Step] + + ProjectDef* = object + cfgFilePath*, defaultBranch*, name*, repo*: string + envVars*: StringTableRef + + StrawBossConfig* = object + artifactsRepo*: string + projects*: seq[ProjectDef] + + +# internal utils + +let nullNode = newJNull() +proc getIfExists(n: JsonNode, key: string): JsonNode = + result = if n.hasKey(key): n[key] + else: nullNode + +# Configuration parsing code + +proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig = + let jsonCfg = parseFile(cfgFile) + + var projectDefs: seq[ProjectDef] = @[] + + for pJson in jsonCfg.getIfExists("projects").getElems: + if not pJson.hasKey("name"): + raiseEx "a project definition is missing the project name" + + if not pJson.hasKey("repo"): + raiseEx "a project definition is missing the project repo configuration" + + var envVars = newStringTable(modeCaseSensitive) + for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("") + + projectDefs.add( + ProjectDef( + cfgFilePath: pJson.getIfExists("cfgFilePath").getStr("strawboss.json"), + defaultBranch: pJson.getIfExists("defaultBranch").getStr("master"), + name: pJson["name"].getStr, + envVars: envVars, + repo: pJson["repo"].getStr)) + + result = StrawBossConfig( + artifactsRepo: jsonCfg.getIfExists("artifactsRepo").getStr("artifacts"), + projects: projectDefs) + +proc loadProjectConfig*(cfgFile: string): ProjectCfg = + let jsonCfg = parseFile(cfgFile) + + if not jsonCfg.hasKey("name"): + raise newException(Exception, "project configuration is missing a name") + + if not jsonCfg.hasKey("steps"): + raise newException(Exception, "project configuration is missing steps definition") + + var steps = initTable[string, Step]() + for sName, pJson in jsonCfg["steps"].getFields: + steps[sName] = Step( + name: sName, + workingDir: pJson.getIfExists("workingDir").getStr("."), + stepCmd: pJson.getIfExists("stepCmd").getStr("sh"), + depends: pJson.getIfExists("depends").getElems.mapIt(it.getStr), + artifacts: pJson.getIfExists("artifacts").getElems.mapIt(it.getStr), + cmdInput: pJson.getIfExists("cmdInput").getElems.mapIt(it.getStr), + expectedEnv: pJson.getIfExists("expectedEnv").getElems.mapIt(it.getStr), + dontCache: pJson.getIfExists("dontCache").getStr("false") != "false") + + if steps[sName].stepCmd == "sh" and steps[sName].cmdInput.len == 0: + warn "Step " & sName & " uses 'sh' as its command but has no cmdInput." + + result = ProjectCfg( + name: jsonCfg["name"].getStr, + versionCmd: jsonCfg.getIfExists("versionCmd").getStr("git describe --tags --always"), + steps: steps) + diff --git a/src/main/nim/strawboss/core.nim b/src/main/nim/strawboss/core.nim new file mode 100644 index 0000000..1c2a920 --- /dev/null +++ b/src/main/nim/strawboss/core.nim @@ -0,0 +1,169 @@ +import logging, nre, os, osproc, sequtils, streams, strtabs, strutils, tables, + tempfile + +import private/util +import configuration + +type + Workspace* = ref object + artifactsRepo*: string + env*: StringTableRef + workingDir*: string + project*: ProjectCfg + + +let ENV = loadEnv() + +let logProcOutput: HandleProcMsgCB = proc (cmd: string, outMsg: TaintedString, errMsg: TaintedString) = + if outMsg != nil: info cmd & "(stdout): " & outMsg + if errMsg != nil: info cmd & "(stderr): " & errMsg + +let envVarRe = re"\$\w+|\$\{[^}]+\}" +proc resolveEnvVars(line: string, env: StringTableRef): string = + result = line + for found in line.findAll(envVarRe): + let key = if found[1] == '{': found[2..^2] else: found[1..^1] + if env.hasKey(key): result = result.replace(found, env[key]) + +let SB_EXPECTED_VARS = ["VERSION"] + +proc setupProjectForWork(projectDef: ProjectDef, buildRef, artifactsRepo: string): Workspace = + + info "Setting up to do work for '" & projectDef.name & "' at ref " & buildRef & "." + var env = ENV + env["GIT_DIR"] = ".git" + + # Create a temp directory that we'll work in + let projDir = mkdtemp() + debug "Workspace for '" & projectDef.name & ": " & projDir + assert projDir.isAbsolute + + # Clone the project into the $temp/repo directory + let cloneResult = exec("git", projDir, ["clone", projectDef.repo, "repo"], + env, {poUsePath}, logProcOutput) + + if cloneResult.exitCode != 0: + removeDir(projDir) + raiseEx "unable to clone repo for '" & projectDef.name & "'" + + # Checkout the requested ref + let checkoutResult = exec("git", projDir & "/repo", ["checkout", buildRef], + env, {poUsePath}, logProcOutput) + + if checkoutResult.exitCode != 0: + removeDir(projDir) + raiseEx "unable to checkout ref " & buildRef & " for '" & projectDef.name & "'" + + # Find the strawboss project configuration + let projCfgFile = projDir & "/repo/" & projectDef.cfgFilePath + if not existsFile(projCfgFile): + removeDir(projDir) + raiseEx "Cannot find strawboss project configuration in the project " & + "repo (expected at '" & projectDef.cfgFilePath & "')." + + let projectCfg = loadProjectConfig(projCfgFile) + result = Workspace(env: env, workingDir: projDir, project: projectCfg, + artifactsRepo: artifactsRepo) + + # Merge in the project-defined env vars + for k, v in projectDef.envVars: result.env[k] = v + + # Get the build version + let versionProc = startProcess( + projectCfg.versionCmd, # command + projDir & "/repo", # working dir + [], # args + result.env, # environment + {poUsePath, poEvalCommand}) # options + + let versionResult = waitForWithOutput(versionProc, logProcOutput, + projectCfg.versionCmd) + + if versionResult.exitCode != 0: + removeDir(projDir) + raiseEx "Version command (" & projectCfg.versionCmd & ") returned non-zero exit code." + + debug "Building version " & versionResult.output.strip + result.env["VERSION"] = versionResult.output.strip + +proc runStep*(step: Step, wksp: Workspace): void = + let stepArtifactDir = wksp.artifactsRepo & "/" & wksp.project.name & "/" & step.name & "/" & wksp.env["VERSION"] + + if existsDir(stepArtifactDir) and not step.dontCache: + info "Skipping step '" & step.name & "': already completed." + return + + info "Running step '" & step.name & "' for " & wksp.project.name + + # Ensure all expected environment variables are present. + for k in (step.expectedEnv & @SB_EXPECTED_VARS): + if not wksp.env.hasKey(k): + debug "workspace.env = " & $(wksp.env) + raiseEx "step " & step.name & " failed: missing required env variable: " & k + + # Ensure that artifacts in steps we depend on are present + for dep in step.depends: + if not wksp.project.steps.hasKey(dep): + raiseEx step.name & " depends on " & dep & " but there is no step named " & dep + let depStep = wksp.project.steps[dep] + + # Run that step (may get skipped) + let depDir = wksp.artifactsRepo & "/" & wksp.project.name & "/" & dep & "/" & wksp.env["VERSION"] + runStep(depStep, wksp) + + # Add the artifacts directory for the dependent step to our env so that + # further steps can reference it via $_DIR + echo "FP: " & depDir + wksp.env[depStep.name & "_DIR"] = depDir + + # Run the step command, piping in cmdInput + debug step.name & ": starting stepCmd: " & step.stepCmd + let cmdProc = startProcess(step.stepCmd, wksp.workingDir & "/repo/" & step.workingDir, + [], wksp.env, {poUsePath, poEvalCommand}) + + let cmdInStream = inputStream(cmdProc) + + # Replace env variables in step cmdInput as we pipe it in + for line in step.cmdInput: cmdInStream.writeLine(line.resolveEnvVars(wksp.env)) + cmdInStream.flush() + cmdInStream.close() + + let cmdResult = waitForWithOutput(cmdProc, logProcOutput, step.stepCmd) + + if cmdResult.exitCode != 0: + raiseEx "step " & step.name & " failed: step command returned non-zero exit code" + + # Gather the output artifacts (if we have any) + if not existsDir(stepArtifactDir): createDir(stepArtifactDir) + if step.artifacts.len > 0: + for a in step.artifacts: + let artifactPath = a.resolveEnvVars(wksp.env) + let artifactName = artifactPath[(artifactPath.rfind("/")+1)..^1] + try: + copyFile(wksp.workingDir & "/repo/" & step.workingDir & "/" & artifactPath, + stepArtifactDir & "/" & artifactName) + except: + removeDir(stepArtifactDir) + raiseEx "step " & step.name & " failed: unable to copy artifact " & + artifactPath & ":\n" & getCurrentExceptionMsg() + +# TODO: change return to return logs, results, some compound, useful object. +proc runStep*(cfg: StrawBossConfig, projectName, stepName, buildRef: string): void = + + let matching = cfg.projects.filterIt(it.name == projectName) + if matching.len == 0: raiseEx "no such project: " & projectName + elif matching.len > 1: raiseEx "more than one project named : " & projectName + + let projectDef = matching[0] + + let foundBuildRef = if buildRef != nil: buildRef else: projectDef.defaultBranch + let wksp = setupProjectForWork(projectDef, foundBuildRef, cfg.artifactsRepo) + + # Find the step + if not wksp.project.steps.hasKey(stepName): + raiseEx "no step name '" & stepName & "' for " & projectName + + let step = wksp.project.steps[stepName] + + runStep(step, wksp) + diff --git a/src/main/nim/strawboss/private/util.nim b/src/main/nim/strawboss/private/util.nim new file mode 100644 index 0000000..491ddfe --- /dev/null +++ b/src/main/nim/strawboss/private/util.nim @@ -0,0 +1,51 @@ +import os, osproc, streams, strtabs + +type HandleProcMsgCB* = proc (cmd: string, outMsg: TaintedString, errMsg: TaintedString): void + +proc raiseEx*(reason: string): void = + raise newException(Exception, reason) + +proc envToTable*(): StringTableRef = + result = newStringTable() + + for k, v in envPairs(): + result[k] = v + +proc waitForWithOutput*(p: Process, msgCB: HandleProcMsgCB, + procCmd: string = ""): + tuple[output: TaintedString, error: TaintedString, exitCode: int] = + + var pout = outputStream(p) + var perr = errorStream(p) + + result = (TaintedString"", TaintedString"", -1) + var line = newStringOfCap(120).TaintedString + while true: + if pout.readLine(line): + if msgCB != nil: msgCB(procCmd, line, nil) + result[0].string.add(line.string) + result[0].string.add("\n") + elif perr.readLine(line): + if msgCB != nil: msgCB(procCmd, nil, line) + result[1].string.add(line.string) + result[1].string.add("\n") + else: + result[2] = peekExitCode(p) + if result[2] != -1: break + close(p) + +proc exec*(command: string, workingDir: string = "", + args: openArray[string] = [], env: StringTableRef = nil, + options: set[ProcessOption] = {poUsePath}, + msgCB: HandleProcMsgCB = nil): + tuple[output: TaintedString, error: TaintedString, exitCode: int] + {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = + + var p = startProcess(command, workingDir, args, env, options) + result = waitForWithOutput(p, msgCb, command) + +proc loadEnv*(): StringTableRef = + result = newStringTable() + + for k, v in envPairs(): + result[k] = v diff --git a/src/main/nim/strawboss/server.nim b/src/main/nim/strawboss/server.nim new file mode 100644 index 0000000..307f247 --- /dev/null +++ b/src/main/nim/strawboss/server.nim @@ -0,0 +1,16 @@ +import asyncdispatch, jester, json + +import ./configuration, ./core + +settings: + port = Port(8180) + +routes: + get "/api/ping": + resp $(%*"pong"), "application/json" + + get "/api/projects": + resp $(%*[]), "application/json" + +proc start*(cfg: StrawBossConfig): void = + runForever() diff --git a/strawboss.nimble b/strawboss.nimble index 7af2d12..182beed 100644 --- a/strawboss.nimble +++ b/strawboss.nimble @@ -5,6 +5,7 @@ version = "0.1.0" author = "Jonathan Bernard" description = "My personal continious integration worker." license = "MIT" +srcDir = "src/main/nim" # Dependencies