github.com/nilium/gitlab-runner@v12.5.0+incompatible/scripts/update-feature-flags-docs/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"strings"
     9  	"text/template"
    10  
    11  	"gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags"
    12  )
    13  
    14  const (
    15  	docsFile = "./docs/configuration/feature-flags.md"
    16  
    17  	startPlaceholder = "<!-- feature_flags_list_start -->"
    18  	endPlaceholder   = "<!-- feature_flags_list_end -->"
    19  )
    20  
    21  var ffTableTemplate = `{{ placeholder "start" }}
    22  
    23  | Feature flag | Default value | Deprecated | To be removed with | Description |
    24  |--------------|---------------|------------|--------------------|-------------|
    25  {{ range $_, $flag := . -}}
    26  | {{ $flag.Name | raw }} | {{ $flag.DefaultValue | raw }} | {{ $flag.Deprecated | tick }} | {{ $flag.ToBeRemovedWith }} | {{ $flag.Description }} |
    27  {{ end }}
    28  {{ placeholder "end" }}
    29  `
    30  
    31  func main() {
    32  	fileContent := getFileContent()
    33  	tableContent := prepareTable()
    34  
    35  	newFileContent := replace(fileContent, tableContent)
    36  
    37  	saveFileContent(newFileContent)
    38  }
    39  
    40  func getFileContent() string {
    41  	data, err := ioutil.ReadFile(docsFile)
    42  	if err != nil {
    43  		panic(fmt.Sprintf("Error while reading file %q: %v", docsFile, err))
    44  	}
    45  
    46  	return string(data)
    47  }
    48  
    49  func prepareTable() string {
    50  	tpl := template.New("ffTable")
    51  	tpl.Funcs(template.FuncMap{
    52  		"placeholder": func(placeholderType string) string {
    53  			switch placeholderType {
    54  			case "start":
    55  				return startPlaceholder
    56  			case "end":
    57  				return endPlaceholder
    58  			default:
    59  				panic(fmt.Sprintf("Undefined placeholder type %q", placeholderType))
    60  			}
    61  		},
    62  		"raw": func(input string) string {
    63  			return fmt.Sprintf("`%s`", input)
    64  		},
    65  		"tick": func(input bool) string {
    66  			if input {
    67  				return "✓"
    68  			}
    69  
    70  			return "✗"
    71  		},
    72  	})
    73  
    74  	tpl, err := tpl.Parse(ffTableTemplate)
    75  	if err != nil {
    76  		panic(fmt.Sprintf("Error while parsing the template: %v", err))
    77  	}
    78  
    79  	buffer := new(bytes.Buffer)
    80  
    81  	err = tpl.Execute(buffer, featureflags.GetAll())
    82  	if err != nil {
    83  		panic(fmt.Sprintf("Error while executing the template: %v", err))
    84  	}
    85  
    86  	return buffer.String()
    87  }
    88  
    89  func replace(fileContent string, tableContent string) string {
    90  	replacer := newBlockLineReplacer(startPlaceholder, endPlaceholder, fileContent, tableContent)
    91  
    92  	newContent, err := replacer.Replace()
    93  	if err != nil {
    94  		panic(fmt.Sprintf("Error while replacing the content: %v", err))
    95  	}
    96  
    97  	return newContent
    98  }
    99  
   100  func saveFileContent(newFileContent string) {
   101  	err := ioutil.WriteFile(docsFile, []byte(newFileContent), 0644)
   102  	if err != nil {
   103  		panic(fmt.Sprintf("Error while writing new content for %q file: %v", docsFile, err))
   104  	}
   105  }
   106  
   107  type blockLineReplacer struct {
   108  	startLine      string
   109  	endLine        string
   110  	replaceContent string
   111  
   112  	input  *bytes.Buffer
   113  	output *bytes.Buffer
   114  
   115  	startFound bool
   116  	endFound   bool
   117  }
   118  
   119  func (r *blockLineReplacer) Replace() (string, error) {
   120  	for {
   121  		line, err := r.input.ReadString('\n')
   122  		if err == io.EOF {
   123  			break
   124  		}
   125  
   126  		if err != nil {
   127  			return "", fmt.Errorf("error while reading issue description: %v", err)
   128  		}
   129  
   130  		r.handleLine(line)
   131  	}
   132  
   133  	return r.output.String(), nil
   134  }
   135  
   136  func (r *blockLineReplacer) handleLine(line string) {
   137  	r.handleStart(line)
   138  	r.handleRewrite(line)
   139  	r.handleEnd(line)
   140  }
   141  
   142  func (r *blockLineReplacer) handleStart(line string) {
   143  	if r.startFound || !strings.Contains(line, r.startLine) {
   144  		return
   145  	}
   146  
   147  	r.startFound = true
   148  }
   149  
   150  func (r *blockLineReplacer) handleRewrite(line string) {
   151  	if r.startFound && !r.endFound {
   152  		return
   153  	}
   154  
   155  	r.output.WriteString(line)
   156  }
   157  
   158  func (r *blockLineReplacer) handleEnd(line string) {
   159  	if !strings.Contains(line, r.endLine) {
   160  		return
   161  	}
   162  
   163  	r.endFound = true
   164  	r.output.WriteString(r.replaceContent)
   165  }
   166  
   167  func newBlockLineReplacer(startLine string, endLine string, input string, replaceContent string) *blockLineReplacer {
   168  	return &blockLineReplacer{
   169  		startLine:      startLine,
   170  		endLine:        endLine,
   171  		input:          bytes.NewBufferString(input),
   172  		output:         new(bytes.Buffer),
   173  		replaceContent: replaceContent,
   174  		startFound:     false,
   175  		endFound:       false,
   176  	}
   177  }