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 }