github.com/swaros/contxt/module/taskrun@v0.0.0-20240305083542-3dbd4436ac40/lint.go (about)

     1  // Copyright (c) 2020 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved.
     2  //
     3  // Licensed under the MIT License
     4  //
     5  //
     6  // Permission is hereby granted, free of charge, to any person obtaining a copy
     7  // of this software and associated documentation files (the "Software"), to deal
     8  // in the Software without restriction, including without limitation the rights
     9  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    10  // copies of the Software, and to permit persons to whom the Software is
    11  // furnished to do so, subject to the following conditions:
    12  //
    13  // The above copyright notice and this permission notice shall be included in all
    14  // copies or substantial portions of the Software.
    15  //
    16  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22  // SOFTWARE.
    23  package taskrun
    24  
    25  import (
    26  	"bytes"
    27  	"errors"
    28  	"fmt"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"github.com/kylelemons/godebug/pretty"
    33  	"github.com/swaros/contxt/module/configure"
    34  	"github.com/swaros/contxt/module/systools"
    35  	"github.com/swaros/manout"
    36  	"gopkg.in/yaml.v3"
    37  )
    38  
    39  func compareContent(a, b interface{}, showBooth bool, size int, right int, noOut bool) bool {
    40  	diffOut := pretty.Compare(a, b)
    41  	diffParts := strings.Split(diffOut, "\n")
    42  	var errors []string
    43  	noDiff := true
    44  	i := 0
    45  	for _, line := range diffParts {
    46  		backColor := manout.BackWhite
    47  		if i%2 == 0 {
    48  			backColor = manout.BackLightGrey
    49  		}
    50  		leftDiff := strings.HasPrefix(line, "+")
    51  		rightDiff := strings.HasPrefix(line, "-")
    52  
    53  		if leftDiff && showBooth {
    54  			lft := getMaxLineString("", size)
    55  			line = getMaxLineString(line, right)
    56  			if !noOut {
    57  				fmt.Println(manout.MessageCln(backColor, manout.ForeYellow, manout.Dim, lft, line))
    58  			}
    59  			i++
    60  		}
    61  		if rightDiff {
    62  			errors = append(errors, "unsupported: "+line)
    63  			rgt := getMaxLineString("  unsupported element ", right)
    64  			line = getMaxLineString(line, size)
    65  			backColor := manout.BackYellow
    66  			if i%2 == 0 {
    67  				backColor = manout.BackLightYellow
    68  			}
    69  			i++
    70  			if !noOut {
    71  				fmt.Println(manout.MessageCln(backColor, manout.BoldTag, manout.ForeDarkGrey, line, manout.ForeRed, manout.BoldTag, rgt))
    72  			}
    73  		}
    74  		if !leftDiff && !rightDiff {
    75  			line = getMaxLineString(line, size+right)
    76  			i++
    77  			if !noOut {
    78  				fmt.Println(manout.MessageCln(backColor, manout.ForeBlue, line))
    79  			}
    80  		}
    81  
    82  	}
    83  
    84  	if len(errors) > 0 {
    85  		noDiff = false
    86  		manout.Error("found unsupported elements.", "count of errors:", len(errors))
    87  	}
    88  
    89  	for _, errMsg := range errors {
    90  		fmt.Println(manout.MessageCln(manout.ForeYellow, errMsg))
    91  	}
    92  	return noDiff
    93  }
    94  
    95  func trySupressDefaults(yamlString string) string {
    96  	ln := "\n"
    97  	outStr := ""
    98  	// first find all defauls values
    99  	lines := strings.Split(yamlString, ln)
   100  	for _, line := range lines {
   101  		checks := strings.Split(line, ": ")
   102  		if len(checks) == 2 {
   103  			// these should have all possible defaults as values.
   104  			if checks[1] != "[]" && checks[1] != "" && checks[1] != "\"\"" && checks[1] != "false" && checks[1] != "0" {
   105  				outStr = outStr + line + ln
   106  			}
   107  		} else {
   108  			outStr = outStr + line + ln
   109  		}
   110  	}
   111  	// next find empty nodes
   112  	lines = strings.Split(outStr, ln)
   113  	newOut := ""
   114  	max := len(lines)
   115  	for index, recheck := range lines {
   116  		if index > 0 && recheck != "" {
   117  
   118  			last := recheck[len(recheck)-1:]
   119  			if last == ":" && index < max {
   120  				nextStr := lines[index+1]
   121  				lastNext := nextStr[len(nextStr)-1:]
   122  				if lastNext != ":" {
   123  					newOut = newOut + recheck + ln
   124  				}
   125  			} else {
   126  				newOut = newOut + recheck + ln
   127  			}
   128  
   129  		} else {
   130  			newOut = newOut + recheck + ln
   131  		}
   132  	}
   133  	return newOut
   134  }
   135  
   136  // ShowAsYaml prints the generated source of the task file
   137  func ShowAsYaml(fullparsed bool, trySupress bool, indent int) {
   138  	template, path, exists, terr := GetTemplate()
   139  	if terr != nil {
   140  		fmt.Println(manout.MessageCln(manout.ForeRed, "Error ", manout.CleanTag, terr.Error()))
   141  		systools.Exit(systools.ErrorTemplateReading)
   142  		return
   143  	}
   144  	var b bytes.Buffer
   145  	if exists {
   146  		if fullparsed {
   147  			yamlEncoder := yaml.NewEncoder(&b)
   148  			yamlEncoder.SetIndent(indent)
   149  			conerr := yamlEncoder.Encode(&template)
   150  			if conerr == nil {
   151  				if trySupress {
   152  					fmt.Println(trySupressDefaults(b.String()))
   153  				} else {
   154  					fmt.Println(b.String())
   155  				}
   156  
   157  			} else {
   158  				manout.Error("error parsing template", conerr)
   159  				systools.Exit(systools.ErrorTemplateReading)
   160  			}
   161  
   162  		} else {
   163  			data, err := GetParsedTemplateSource(path)
   164  			if err != nil {
   165  				manout.Error("template loading", err)
   166  				systools.Exit(systools.ErrorTemplateReading)
   167  			}
   168  			fmt.Println(data)
   169  		}
   170  	}
   171  }
   172  
   173  func TestTemplate() error {
   174  	if template, _, exists, terr := GetTemplate(); terr != nil {
   175  		return terr
   176  	} else {
   177  		if !exists {
   178  			GetLogger().Debug("no template exists to check")
   179  		} else {
   180  			// check version
   181  			// if they is not matching, we die with an error
   182  			if !configure.CheckCurrentVersion(template.Version) {
   183  				return errors.New("unsupported version " + template.Version)
   184  			}
   185  		}
   186  	}
   187  	return nil
   188  }
   189  
   190  // LintOut prints the source code and the parsed content
   191  // in a table view, and marks configured and not configured entries
   192  // with dfferent colors
   193  func LintOut(leftcnt, rightcnt int, all bool, noOut bool) bool {
   194  	template, path, exists, terr := GetTemplate()
   195  	if terr != nil {
   196  		manout.Error("ERROR", terr.Error())
   197  		return false
   198  	}
   199  	if exists && rightcnt >= 0 && leftcnt >= 0 {
   200  		data, err := GetParsedTemplateSource(path)
   201  		if err != nil {
   202  			manout.Error("template loading", err)
   203  			return false
   204  		}
   205  		origMap, yerr := YAMLToMap(data)
   206  		if yerr == nil {
   207  			conversionres, conerr := yaml.Marshal(template)
   208  			if conerr == nil {
   209  				m := make(map[string]interface{})
   210  				amlerr := yaml.Unmarshal(conversionres, &m)
   211  				if amlerr != nil {
   212  					fmt.Println(amlerr)
   213  					systools.Exit(1)
   214  				}
   215  
   216  				return compareContent(origMap, m, all, leftcnt, rightcnt, noOut)
   217  			}
   218  
   219  		} else {
   220  			prinfFile(path, leftcnt+rightcnt)
   221  			manout.Error("parsing error", yerr)
   222  		}
   223  
   224  	} else {
   225  		manout.Error("template not found ", path)
   226  	}
   227  	return false
   228  }
   229  
   230  func getMaxLineString(line string, length int) string {
   231  	if len(line) < length {
   232  		for i := len(line); i < length; i++ {
   233  			line = line + " "
   234  		}
   235  	}
   236  	if len(line) > length {
   237  		line = line[0:length]
   238  	}
   239  	return line
   240  }
   241  
   242  func prinfFile(filename string, size int) error {
   243  	data, err := GetParsedTemplateSource(filename)
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	backColor := manout.BackWhite
   249  	lines := strings.Split(data, "\n")
   250  	i := 0
   251  	for _, line := range lines {
   252  		i++
   253  		prefix := getMaxLineString(strconv.Itoa(i), 5)
   254  		line = getMaxLineString(line, size)
   255  		fmt.Println(manout.MessageCln(manout.BackCyan, manout.ForeWhite, prefix, backColor, manout.ForeBlue, line))
   256  	}
   257  	return nil
   258  }