diff --git a/model.go b/helpers/cv.go similarity index 98% rename from model.go rename to helpers/cv.go index 6724f06..5c79830 100644 --- a/model.go +++ b/helpers/cv.go @@ -1,4 +1,4 @@ -package main +package helpers type Cv struct { FirstName string `json:"firstName"` diff --git a/helpers/helpers.go b/helpers/helpers.go new file mode 100644 index 0000000..1c27ca4 --- /dev/null +++ b/helpers/helpers.go @@ -0,0 +1,86 @@ +package helpers + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "text/template" +) + +const ( + defaultImageName string = "cv-builder" + defaultImageTag string = "latest" +) + +// ImageName generates the docker image name from the environment +// variables IMAGE_NAME and IMAGE_TAG. Default vaules will be used +// if these variables are not set or empty. +func ImageName() string { + name := os.Getenv("IMAGE_NAME") + if len(name) == 0 { + name = defaultImageName + } + + tag := os.Getenv("IMAGE_TAG") + if len(tag) == 0 { + tag = defaultImageTag + } + + return name + ":" + tag +} + +// CreateCVTex generates the CV docuemnt as a Tex file. +func CreateCVTex(cvJsonDataFile, cvTemplateDir, cvOutputDir, cvOutputFileName string) error { + fmt.Printf("INFO: Attempting to read data from %s...\n", cvJsonDataFile) + + data, err := ioutil.ReadFile(cvJsonDataFile) + if err != nil { + return fmt.Errorf("ERROR: Unable to read data from file. %s\n", err.Error()) + } + fmt.Println("INFO: Successfully read data.") + + fmt.Println("INFO: Attempting to unmarshal JSON data...") + + var cv Cv + if err = json.Unmarshal(data, &cv); err != nil { + return fmt.Errorf("ERROR: Unable to unmarshal JSON data. %s\n", err.Error()) + } + fmt.Println("INFO: JSON unmarshalling was successful.") + + // if CV_CONTACT_PHONE is set then add it to the CV + phone := os.Getenv("CV_CONTACT_PHONE") + if len(phone) > 0 { + cv.Contact.Phone = phone + } + + cvOutputPath := cvOutputDir + "/" + cvOutputFileName + fmt.Printf("INFO: Attempting to create output file %s...\n", cvOutputPath) + + if err = os.MkdirAll(cvOutputDir, 0750); err != nil { + return fmt.Errorf("ERROR: Unable to create output directory %s. %s\n", cvOutputDir, err.Error()) + } + + output, err := os.Create(cvOutputPath) + if err != nil { + return fmt.Errorf("ERROR: Unable to create output file %s. %s\n", cvOutputPath, err.Error()) + } + fmt.Printf("INFO: Successfully created output file %s.\n", cvOutputPath) + defer output.Close() + + fmt.Println("INFO: Attempting template execution...") + fmap := template.FuncMap{ + "notLastElement": notLastElement, + "join": join, + "durationToString": durationToString, + } + + t := template.Must(template.New("cv.tmpl.tex").Funcs(fmap).Delims("<<", ">>").ParseGlob(cvTemplateDir + "*.tmpl.tex")) + + if err = t.Execute(output, cv); err != nil { + return fmt.Errorf("ERROR: Unable to execute the CV template. %s\n", err.Error()) + } + fmt.Println("INFO: Template execution successful.") + + return nil +} diff --git a/helpers/templatefuncs.go b/helpers/templatefuncs.go new file mode 100644 index 0000000..f2ac1fb --- /dev/null +++ b/helpers/templatefuncs.go @@ -0,0 +1,34 @@ +package helpers + +import ( + "fmt" + "strings" +) + +// notLastElement returns true if an element of an array +// or a slice is not the last. +func notLastElement(pos, length int) bool { + return pos < length-1 +} + +// join uses strings.Join to join all string elements into +// a single string. +func join(s []string) string { + return strings.Join(s, " ") +} + +// durationToString outputs the employment/education +// duration as a formatted string. +func durationToString(d Duration) string { + start := fmt.Sprintf("%s, %s", d.Start.Month, d.Start.Year) + present := strings.ToLower(d.Present) + end := "" + + if present == "yes" || present == "true" { + end = "Present" + } else { + end = fmt.Sprintf("%s, %s", d.End.Month, d.End.Year) + } + + return start + " - " + end +} diff --git a/magefile.go b/magefile.go index 1110dd2..38a3515 100644 --- a/magefile.go +++ b/magefile.go @@ -6,22 +6,22 @@ import ( "fmt" "io" "io/ioutil" - "os" "os/exec" "strings" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" + "gitlab.com/dananglin/cv/helpers" ) const ( aspellLang string = "en_GB" aspellPersonalWordlist string = "./.aspell/.aspell.en.pws" cvJsonDataFile string = "./data/cv.json" - defaultImageName string = "cv-builder" - defaultImageTag string = "latest" + cvOutputDir string = "./__output" + cvTemplateDir string = "./template/tex/" + cvOutputFileName string = "cv.tex" dockerfile string = "./docker/Dockerfile" - outputDir string = "./__output" ) // Default sets the default target. @@ -87,10 +87,10 @@ func SpellCheck() error { // template. func Tex() error { fmt.Println("Generating the tex file...") + mg.Deps(Clean) - gocmd := mg.GoCmd() - return sh.Run(gocmd, "run", ".") + return helpers.CreateCVTex(cvJsonDataFile, cvTemplateDir, cvOutputDir, cvOutputFileName) } // Pdf takes the output tex file generated by the Tex target and @@ -98,7 +98,7 @@ func Tex() error { func Pdf() error { mg.Deps(Tex) - pathArg := "--path=" + outputDir + pathArg := "--path=" + cvOutputDir fmt.Println("Generating the PDF file...") return sh.Run("mtxrun", pathArg, "--script", "context", "cv.tex") @@ -106,7 +106,7 @@ func Pdf() error { // Image builds the CV builder docker image. func Image() error { - image := imageName() + image := helpers.ImageName() return sh.Run("docker", "build", "-f", dockerfile, "-t", image, ".") } @@ -116,33 +116,16 @@ func Image() error { func PublishImage() error { mg.Deps(Image) - image := imageName() + image := helpers.ImageName() return sh.Run("docker", "push", image) } // Clean removes the directory where the output files // are generated. func Clean() error { - if err := sh.Rm(outputDir); err != nil { + if err := sh.Rm(cvOutputDir); err != nil { return err } return nil } - -// imageName generates the docker image name from the environment -// variables IMAGE_NAME and IMAGE_TAG. Default vaules will be used -// if these variables are not set or empty. -func imageName() string { - name := os.Getenv("IMAGE_NAME") - if len(name) == 0 { - name = defaultImageName - } - - tag := os.Getenv("IMAGE_TAG") - if len(tag) == 0 { - tag = defaultImageTag - } - - return name + ":" + tag -} diff --git a/main.go b/main.go deleted file mode 100644 index a228c06..0000000 --- a/main.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "strings" - "text/template" -) - -var ( - cvDataPath string = "data/cv.json" - cvTemplateDir string = "template/tex/" - cvOutputDir string = "__output/" - cvOutputFileName string = "cv.tex" -) - -func main() { - - var cv Cv - cvOutputPath := cvOutputDir + cvOutputFileName - - fmap := template.FuncMap{ - "notLastElement": notLastElement, - "join": join, - "durationToString": durationToString, - } - - // Read the JSON data - log.Printf("INFO: Attempting to read data from %s...", cvDataPath) - data, err := ioutil.ReadFile(cvDataPath) - if err != nil { - log.Fatalf("ERROR: Unable to read data from file. %s", err.Error()) - } - log.Print("INFO: Successfully read data.") - - log.Printf("INFO: Attempting to unmarshal JSON data...") - if err = json.Unmarshal(data, &cv); err != nil { - log.Fatalf("ERROR: Unable to unmarshal JSON data. %s", err.Error()) - } - log.Println("INFO: JSON unmarshalling was successful.") - - // if CV_CONTACT_PHONE is set then add it to the CV - phone := os.Getenv("CV_CONTACT_PHONE") - if len(phone) > 0 { - cv.Contact.Phone = phone - } - - // Create the Output tex file - log.Printf("INFO: Attempting to create output file %s...", cvOutputPath) - if err = os.MkdirAll(cvOutputDir, 0750); err != nil { - log.Fatalf("ERROR: Unable to create output directory %s. %s", cvOutputDir, err.Error()) - } - output, err := os.Create(cvOutputPath) - if err != nil { - log.Fatalf("ERROR: Unable to create output file %s. %s.", cvOutputPath, err.Error()) - } - defer output.Close() - log.Printf("INFO: Successfully created output file %s.", cvOutputPath) - - // Execute template engine and produce the resulting TEX file - log.Println("INFO: Attempting template execution...") - t := template.Must(template.New("cv.tmpl.tex").Funcs(fmap).Delims("<<", ">>").ParseGlob(cvTemplateDir + "*.tmpl.tex")) - - if err = t.Execute(output, cv); err != nil { - log.Fatalf("ERROR: Unable to execute the CV template. %s", err.Error()) - } - log.Println("INFO: Template execution successful.") -} - -// notLastElement returns true if an element of an array -// or a slice is not the last. -func notLastElement(pos, length int) bool { - return pos < length-1 -} - -// join uses strings.Join to join all string elements into -// a single string. -func join(s []string) string { - return strings.Join(s, " ") -} - -// durationToString outputs the employment/education -// duration as a formatted string. -func durationToString(d Duration) string { - start := fmt.Sprintf("%s, %s", d.Start.Month, d.Start.Year) - present := strings.ToLower(d.Present) - end := "" - - if present == "yes" || present == "true" { - end = "Present" - } else { - end = fmt.Sprintf("%s, %s", d.End.Month, d.End.Year) - } - - return start + " - " + end -}