github.com/SAP/cloud-mta-build-tool@v1.2.27/internal/artifacts/manifest_test.go (about) 1 package artifacts 2 3 import ( 4 "os" 5 "strings" 6 "text/template" 7 8 . "github.com/onsi/ginkgo" 9 . "github.com/onsi/ginkgo/extensions/table" 10 . "github.com/onsi/gomega" 11 "github.com/pkg/errors" 12 13 "github.com/SAP/cloud-mta-build-tool/internal/archive" 14 "github.com/SAP/cloud-mta-build-tool/internal/buildops" 15 "github.com/SAP/cloud-mta-build-tool/internal/commands" 16 "github.com/SAP/cloud-mta-build-tool/internal/conttype" 17 "github.com/SAP/cloud-mta-build-tool/internal/version" 18 "github.com/SAP/cloud-mta/mta" 19 ) 20 21 var _ = Describe("manifest", func() { 22 23 BeforeEach(func() { 24 createDirInTmpFolder("mta", "META-INF") 25 }) 26 27 AfterEach(func() { 28 Ω(os.RemoveAll(getTestPath("result"))).Should(Succeed()) 29 }) 30 31 var _ = Describe("setManifestDesc", func() { 32 It("Sanity", func() { 33 createDirInTmpFolder("mta", "node-js") 34 createFileInTmpFolder("mta", "node-js", "data.zip") 35 createFileInTmpFolder("mta", "config-site-host.json") 36 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath()} 37 mtaObj, err := loc.ParseFile() 38 Ω(err).Should(Succeed()) 39 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 40 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 41 golden := getFileContent(getTestPath("golden_manifest.mf")) 42 v, _ := version.GetVersion() 43 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 44 Ω(actual).Should(Equal(golden)) 45 }) 46 It("Unknown content type, assembly scenario", func() { 47 createDirInTmpFolder("mta", "node-js") 48 createFileInTmpFolder("mta", "node-js", "server.js") 49 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), Descriptor: dir.Dep} 50 mtaObj, err := loc.ParseFile() 51 Ω(err).Should(Succeed()) 52 err = setManifestDesc(&loc, &loc, &loc, true, mtaObj.Modules, []*mta.Resource{}, "cf") 53 checkError(err, conttype.ContentTypeUndefinedMsg, ".js") 54 }) 55 It("Sanity - with configuration provided", func() { 56 // no_source module (with no-source build parameter) is not referenced in the manifest 57 createDirInTmpFolder("mta", "node-js") 58 createFileInTmpFolder("mta", "node-js", "data.zip") 59 createFileInTmpFolder("mta", "config-site-host.json") 60 createFileInTmpFolder("mta", "config-site-host1.json") 61 createFileInTmpFolder("mta", "xs-security.json") 62 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mta_cfg.yaml"} 63 mtaObj, err := loc.ParseFile() 64 Ω(err).Should(Succeed()) 65 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 66 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 67 golden := getFileContent(getTestPath("golden_manifest_cfg.mf")) 68 v, _ := version.GetVersion() 69 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 70 Ω(actual).Should(Equal(golden)) 71 }) 72 It("wrong Commands configuration", func() { 73 createDirInTmpFolder("mta", "node-js") 74 createFileInTmpFolder("mta", "node-js", "data.zip") 75 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath()} 76 mtaObj, err := loc.ParseFile() 77 Ω(err).Should(Succeed()) 78 moduleConf := commands.ModuleTypeConfig 79 commands.ModuleTypeConfig = []byte("bad module conf") 80 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(HaveOccurred()) 81 commands.ModuleTypeConfig = moduleConf 82 }) 83 It("module with defined build-result fails when build-result file does not exist in source directory", func() { 84 createDirInTmpFolder("mta", "node-js") 85 createFileInTmpFolder("mta", "node-js", "some.war") 86 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaWrongBuildResult.yaml"} 87 mtaObj, err := loc.ParseFile() 88 Ω(err).Should(Succeed()) 89 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(HaveOccurred()) 90 }) 91 It("module with defined build-result fails when build-result file does not exist in target temp directory", func() { 92 createDirInTmpFolder("mta", "node-js") 93 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaWrongBuildResult2.yaml"} 94 mtaObj, err := loc.ParseFile() 95 Ω(err).Should(Succeed()) 96 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(HaveOccurred()) 97 }) 98 It("entry for module with defined build-result has the build-result file", func() { 99 createDirInTmpFolder("mta", "node-js") 100 createFileInTmpFolder("mta", "node-js", "data1.zip") 101 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildResult.yaml"} 102 mtaObj, err := loc.ParseFile() 103 Ω(err).Should(Succeed()) 104 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 105 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 106 golden := getFileContent(getTestPath("golden_manifestBuildResult.mf")) 107 v, _ := version.GetVersion() 108 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 109 Ω(actual).Should(Equal(golden)) 110 }) 111 It("wrong content types configuration", func() { 112 createDirInTmpFolder("mta", "node-js") 113 createFileInTmpFolder("mta", "node-js", "data.zip") 114 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath()} 115 mtaObj, err := loc.ParseFile() 116 Ω(err).Should(Succeed()) 117 contentTypesOrig := conttype.ContentTypeConfig 118 conttype.ContentTypeConfig = []byte(`wrong configuraion`) 119 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(HaveOccurred()) 120 conttype.ContentTypeConfig = contentTypesOrig 121 122 }) 123 It("Sanity - module with no path is not added to the manifest", func() { 124 createDirInTmpFolder("mta", "node-js") 125 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mta_no_paths.yaml"} 126 mtaObj, err := loc.ParseFile() 127 Ω(err).Should(Succeed()) 128 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 129 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 130 golden := getFileContent(getTestPath("golden_assembly_manifest_no_paths.mf")) 131 v, _ := version.GetVersion() 132 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 133 Ω(actual).Should(Equal(golden)) 134 }) 135 It("With resources", func() { 136 createDirInTmpFolder("assembly-sample", "META-INF") 137 createDirInTmpFolder("assembly-sample", "web") 138 createFileInTmpFolder("assembly-sample", "config-site-host.json") 139 createFileInTmpFolder("assembly-sample", "xs-security.json") 140 createDirInTmpFolder("assembly-sample", "inner") 141 createFileInTmpFolder("assembly-sample", "inner", "uaa.json") 142 createFileInTmpFolder("assembly-sample", "inner", "xs-security.json") 143 loc := dir.Loc{SourcePath: getTestPath("assembly-sample"), TargetPath: getResultPath(), Descriptor: "dep"} 144 mtaObj, err := loc.ParseFile() 145 Ω(err).Should(Succeed()) 146 Ω(setManifestDesc(&loc, &loc, &loc, true, mtaObj.Modules, mtaObj.Resources, "cf")).Should(Succeed()) 147 actual := getFileContent(getFullPathInTmpFolder("assembly-sample", "META-INF", "MANIFEST.MF")) 148 golden := getFileContent(getTestPath("golden_assembly_manifest.mf")) 149 v, _ := version.GetVersion() 150 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 151 Ω(actual).Should(Equal(golden)) 152 }) 153 It("With missing module path", func() { 154 createDirInTmpFolder("assembly-sample", "META-INF") 155 loc := dir.Loc{SourcePath: getTestPath("assembly-sample"), TargetPath: getResultPath(), Descriptor: "dep"} 156 mtaObj, err := loc.ParseFile() 157 Ω(err).Should(Succeed()) 158 err = setManifestDesc(&loc, &loc, &loc, true, mtaObj.Modules, mtaObj.Resources, "cf") 159 checkError(err, wrongArtifactPathMsg, "java-hello-world") 160 }) 161 It("With missing resource", func() { 162 createDirInTmpFolder("assembly-sample", "META-INF") 163 createDirInTmpFolder("assembly-sample", "web") 164 createFileInTmpFolder("assembly-sample", "config-site-host.json") 165 createDirInTmpFolder("assembly-sample", "inner") 166 createFileInTmpFolder("assembly-sample", "inner", "uaa.json") 167 createFileInTmpFolder("assembly-sample", "inner", "xs-security.json") 168 loc := dir.Loc{SourcePath: getTestPath("assembly-sample"), TargetPath: getTestPath("result"), Descriptor: "dep"} 169 mtaObj, err := loc.ParseFile() 170 Ω(err).Should(Succeed()) 171 err = setManifestDesc(&loc, &loc, &loc, true, mtaObj.Modules, mtaObj.Resources, "cf") 172 checkError(err, unknownResourceContentTypeMsg, "java-uaa") 173 174 }) 175 It("required resource with path fails when the path doesn't exist", func() { 176 createDirInTmpFolder("assembly-sample", "META-INF") 177 createDirInTmpFolder("assembly-sample", "web") 178 createFileInTmpFolder("assembly-sample", "xs-security.json") 179 loc := dir.Loc{SourcePath: getTestPath("assembly-sample"), TargetPath: getTestPath("result"), Descriptor: "dep"} 180 mtaObj, err := loc.ParseFile() 181 Ω(err).Should(Succeed()) 182 err = setManifestDesc(&loc, &loc, &loc, true, mtaObj.Modules, mtaObj.Resources, "cf") 183 // This fails because the config-site-host.json file (from the path of the required java-site-host) doesn't exist 184 checkError(err, requiredEntriesProblemMsg, "java-hello-world-backend") 185 }) 186 When("build-artifact-name is defined in the build parameters", func() { 187 It("should take the defined build artifact name when the build artifact exists", func() { 188 createDirInTmpFolder("mta", "node-js") 189 createFileInTmpFolder("mta", "node-js", "data2.zip") 190 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifact.yaml"} 191 mtaObj, err := loc.ParseFile() 192 Ω(err).Should(Succeed()) 193 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 194 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 195 golden := getFileContentWithCliVersion(getTestPath("golden_manifestBuildArtifact.mf")) 196 Ω(actual).Should(Equal(golden)) 197 }) 198 It("should take the archive.zip with the build artifact name when the build result is a folder", func() { 199 createDirInTmpFolder("mta", "node-js") 200 createFileInTmpFolder("mta", "node-js", "data2.zip") 201 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifact.yaml"} 202 mtaObj, err := loc.ParseFile() 203 Ω(err).Should(Succeed()) 204 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 205 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 206 golden := getFileContentWithCliVersion(getTestPath("golden_manifestBuildArtifact.mf")) 207 Ω(actual).Should(Equal(golden)) 208 }) 209 It("should skip the module when it has no path", func() { 210 createDirInTmpFolder("mta", "node-js", "data2") 211 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifactNoPath.yaml"} 212 mtaObj, err := loc.ParseFile() 213 Ω(err).Should(Succeed()) 214 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 215 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 216 golden := getFileContentWithCliVersion(getTestPath("golden_assembly_manifest_no_paths.mf")) 217 Ω(actual).Should(Equal(golden)) 218 }) 219 It("should take the build artifact name when the build result is also defined", func() { 220 createDirInTmpFolder("mta", "node-js") 221 createFileInTmpFolder("mta", "node-js", "ROOT.war") 222 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildResultAndArtifact.yaml"} 223 mtaObj, err := loc.ParseFile() 224 Ω(err).Should(Succeed()) 225 Ω(setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf")).Should(Succeed()) 226 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 227 golden := getFileContentWithCliVersion(getTestPath("golden_manifestBuildResultAndArtifact.mf")) 228 Ω(actual).Should(Equal(golden)) 229 }) 230 It("should fail when build-artifact-name is not a string value", func() { 231 createDirInTmpFolder("mta", "node-js") 232 createFileInTmpFolder("mta", "node-js", "data.zip") 233 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifactBad.yaml"} 234 mtaObj, err := loc.ParseFile() 235 Ω(err).Should(Succeed()) 236 err = setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf") 237 checkError(err, buildops.WrongBuildArtifactNameMsg, "1", "node-js") 238 }) 239 It("should fail when data.zip exists instead of the build artifact name", func() { 240 createDirInTmpFolder("mta", "node-js") 241 createFileInTmpFolder("mta", "node-js", "data.zip") 242 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifact.yaml"} 243 mtaObj, err := loc.ParseFile() 244 Ω(err).Should(Succeed()) 245 err = setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf") 246 checkError(err, wrongArtifactPathMsg, "node-js") 247 }) 248 It("should fail when the build artifact doesn't exist in the module folder", func() { 249 createDirInTmpFolder("mta", "node-js") 250 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifact.yaml"} 251 mtaObj, err := loc.ParseFile() 252 Ω(err).Should(Succeed()) 253 err = setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf") 254 checkError(err, wrongArtifactPathMsg, "node-js") 255 }) 256 It("should fail when the module folder doesn't exist", func() { 257 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath(), MtaFilename: "mtaBuildArtifact.yaml"} 258 mtaObj, err := loc.ParseFile() 259 Ω(err).Should(Succeed()) 260 err = setManifestDesc(&loc, &loc, &loc, false, mtaObj.Modules, []*mta.Resource{}, "cf") 261 checkError(err, wrongArtifactPathMsg, "node-js") 262 }) 263 }) 264 }) 265 266 var _ = Describe("genManifest", func() { 267 It("Sanity", func() { 268 loc := dir.Loc{SourcePath: getTestPath("mta"), TargetPath: getResultPath()} 269 entries := []entry{ 270 { 271 EntryName: "node-js", 272 EntryPath: "node-js/data.zip", 273 EntryType: moduleEntry, 274 ContentType: "application/zip", 275 }, 276 } 277 Ω(genManifest(loc.GetManifestPath(), entries)).Should(Succeed()) 278 actual := getFileContent(getFullPathInTmpFolder("mta", "META-INF", "MANIFEST.MF")) 279 golden := getFileContent(getTestPath("golden_manifest.mf")) 280 v, _ := version.GetVersion() 281 golden = strings.Replace(golden, "{{cli_version}}", v.CliVersion, -1) 282 Ω(actual).Should(Equal(golden)) 283 }) 284 It("Fails on wrong location", func() { 285 loc := dir.Loc{} 286 Ω(genManifest(loc.GetManifestPath(), []entry{})).Should(HaveOccurred()) 287 }) 288 It("Fails on wrong version configuration", func() { 289 versionCfg := version.VersionConfig 290 version.VersionConfig = []byte(` 291 bad config 292 `) 293 loc := dir.Loc{} 294 Ω(genManifest(loc.GetManifestPath(), []entry{})).Should(HaveOccurred()) 295 version.VersionConfig = versionCfg 296 }) 297 }) 298 var _ = Describe("moduleDefined", func() { 299 It("not defined", func() { 300 Ω(moduleDefined("x", []string{"a"})).Should(BeFalse()) 301 }) 302 It("empty list", func() { 303 Ω(moduleDefined("x", []string{})).Should(BeTrue()) 304 }) 305 It("defined", func() { 306 Ω(moduleDefined("x", []string{"y", "x"})).Should(BeTrue()) 307 }) 308 309 }) 310 311 var _ = Describe("populateManifest", func() { 312 It("Nil file failure", func() { 313 314 Ω(populateManifest(&testWriter{}, template.FuncMap{})).Should(HaveOccurred()) 315 }) 316 }) 317 318 var _ = Describe("buildEntries", func() { 319 It("Sanity", func() { 320 createDirInTmpFolder("result", "node-js") 321 mod := mta.Module{Name: "module1"} 322 requires := []mta.Requires{ 323 { 324 Name: "req", 325 Parameters: map[string]interface{}{ 326 "path": "node-js", 327 }, 328 }, 329 } 330 entries, err := buildEntries(&dir.Loc{SourcePath: getTestPath("result"), TargetPath: getTestPath("result")}, &mod, requires, &conttype.ContentTypes{}) 331 Ω(len(entries)).Should(Equal(1)) 332 Ω(err).Should(Succeed()) 333 e := entries[0] 334 Ω(e.EntryType).Should(Equal(requiredEntry)) 335 Ω(e.EntryName).Should(Equal("module1/req")) 336 Ω(e.EntryPath).Should(Equal("node-js")) 337 Ω(e.ContentType).Should(Equal(dirContentType)) 338 }) 339 340 }) 341 342 DescribeTable("mergeDuplicateEntries", func(entries []entry, expected []entry) { 343 actual := mergeDuplicateEntries(entries) 344 Ω(actual).Should(Equal(expected)) 345 }, 346 Entry("returns non-module entries unchanged", []entry{ 347 {EntryName: "e1", EntryPath: "a", EntryType: resourceEntry, ContentType: "t"}, 348 {EntryName: "e2", EntryPath: "a", EntryType: requiredEntry, ContentType: "t"}, 349 {EntryName: "e3", EntryPath: "b", EntryType: resourceEntry, ContentType: "t"}, 350 {EntryName: "e4", EntryPath: "b", EntryType: resourceEntry, ContentType: "t"}, 351 }, []entry{ 352 {EntryName: "e1", EntryPath: "a", EntryType: resourceEntry, ContentType: "t"}, 353 {EntryName: "e2", EntryPath: "a", EntryType: requiredEntry, ContentType: "t"}, 354 {EntryName: "e3", EntryPath: "b", EntryType: resourceEntry, ContentType: "t"}, 355 {EntryName: "e4", EntryPath: "b", EntryType: resourceEntry, ContentType: "t"}, 356 }, 357 ), 358 Entry("merges module entries with the same path and keeps the modules order", []entry{ 359 {EntryName: "e1", EntryPath: "a", EntryType: moduleEntry, ContentType: "t1"}, 360 {EntryName: "e2", EntryPath: "a", EntryType: moduleEntry, ContentType: "t2"}, 361 {EntryName: "e3", EntryPath: "a", EntryType: moduleEntry, ContentType: "t3"}, 362 {EntryName: "e4", EntryPath: "b", EntryType: moduleEntry, ContentType: "t4"}, 363 {EntryName: "e5", EntryPath: "b", EntryType: moduleEntry, ContentType: "t5"}, 364 {EntryName: "e6", EntryPath: "c", EntryType: moduleEntry, ContentType: "t6"}, 365 }, []entry{ 366 {EntryName: "e1, e2, e3", EntryPath: "a", EntryType: moduleEntry, ContentType: "t1"}, 367 {EntryName: "e4, e5", EntryPath: "b", EntryType: moduleEntry, ContentType: "t4"}, 368 {EntryName: "e6", EntryPath: "c", EntryType: moduleEntry, ContentType: "t6"}, 369 }, 370 ), 371 Entry("merges module entries and keeps non-module entries unchanged at the end", []entry{ 372 {EntryName: "e1", EntryPath: "a", EntryType: resourceEntry, ContentType: "t1"}, 373 {EntryName: "e2", EntryPath: "a", EntryType: moduleEntry, ContentType: "t2"}, 374 {EntryName: "e3", EntryPath: "a", EntryType: moduleEntry, ContentType: "t3"}, 375 {EntryName: "e4", EntryPath: "b", EntryType: moduleEntry, ContentType: "t4"}, 376 {EntryName: "e5", EntryPath: "b", EntryType: requiredEntry, ContentType: "t5"}, 377 }, []entry{ 378 {EntryName: "e2, e3", EntryPath: "a", EntryType: moduleEntry, ContentType: "t2"}, 379 {EntryName: "e4", EntryPath: "b", EntryType: moduleEntry, ContentType: "t4"}, 380 {EntryName: "e1", EntryPath: "a", EntryType: resourceEntry, ContentType: "t1"}, 381 {EntryName: "e5", EntryPath: "b", EntryType: requiredEntry, ContentType: "t5"}, 382 }, 383 ), 384 ) 385 386 var _ = Describe("getContentType", func() { 387 It("fails on empty path", func() { 388 _, err := getContentType("", &conttype.ContentTypes{}) 389 Ω(err).Should(HaveOccurred()) 390 }) 391 }) 392 }) 393 394 type testWriter struct { 395 } 396 397 func (t *testWriter) Write(p []byte) (n int, err error) { 398 return 0, errors.New("err") 399 } 400 401 func getFileContentWithCliVersion(path string) string { 402 content := getFileContent(path) 403 v, _ := version.GetVersion() 404 content = strings.Replace(content, "{{cli_version}}", v.CliVersion, -1) 405 return content 406 }