How to get all files recursively by extension using filepath.Glob()? - go

I am trying to parse all the files with an extension with the text/template parser, which uses the filepath.Glob() function under the hood to match files.
package main
//go:embed templates
var templates embed.FS
func main() {
folder, _ := fs.Sub(t, "templates")
tpl, err := template.ParseFS(folder, "**/*.gohtml")
// ...
}
As far as I can tell, there isn't a specification defining the behaviour of glob patterns, however this pattern in Node.js will match all files in a directory with the .gohtml extension.
It doesn't seem to match all files in Go, however manually adding folders selects the correct files.
tpl, err := template.ParseFS(folder, "*.gohtml", "partials/*.gohtml")
I tried looking at the documentation on the Go website but struggled to find resources on the pattern matching rules.
I'm hoping it's as simple as getting the right variation of *s, /s and I just haven't found it

Related

How to access custom options after protoc -ofile_descriptor_set from Golang?

Is there a way to access the contents of a custom options extension from a descriptorpb.DescriptorProto read out of a FileDescriptorSet?
First, I defined a custom option in a .proto file and compiled that source file as a Go import.
Second, I imported the option's .proto file in a separate .proto file and used it, then used protoc -omymessage.fds to write a FileDescriptorSet to a file.
Problem: I cannot find a way to read my custom options extension when starting from the FileDescriptorSet.
I tried code similar to this, because I couldn't find any other way to access extended options. I feel like there should be an accessor directly on the DescriptorProto representing the message, but there isn`t.
var fds descriptorpb.FileDescriptorSet
proto.Unmarshal(data, &fds) // Yes I'm checking for errors
for _, file := range fds.File {
for _, msg := range file.MessageType {
hasOpts := proto.HasExtension(msg.ProtoReflect().Descriptor().Options(), myoptions.E_MyOptions)
// hasOpts is always false
}
}

Find location of Golang application

I have a Golang application which is being deployed into a container via Kubernetes. Within the service I would like to read a file which is nested within the application.
I'm struggling to find out how to set the current working directory to the location of the application.
Currently the application panics with an error due to the relative paths not being aligned:
open /workspace/source/package/file.csv: no such file or directory
I've tried using the following code and appending the file name:
var (
_, b, _, _ = runtime.Caller(0)
basepath = filepath.Dir(b)
)
However this still doesn't work. Is there a way to find the applications current location and then append the filename?
Thanks for help!
Use os.Executable to find the path to the executable.
b, err := os.Executable()
if err != nil {
log.Fatal(err)
}
basepath := filepath.Dir(b)
Can't you just cd before executing the application? Then you can use relative paths.

Read file from relative path with different callers

I'm trying to read from a file in my project's directory.
My problem is, that depending on the caller, the path changes. The caller changes, because I want to unit test this code and the caller is not Main.go anymore.
This is what my project structure looks like:
The code where I try to access specialChars.txt from looks like this:
func RemoveSpecialChars(word string) string {
file, err := ioutil.ReadFile("wordlists/specialChars.txt")
[...]
}
This code works for the start from Main.go but not for the start from CleanupUtil_test.go. To get it working from the test I would need file, err := ioutil.ReadFile("../wordlists/specialChars.txt")
I found answers like this one: https://stackoverflow.com/a/32163888/2837489
_, filename, _, ok := runtime.Caller(0) which is obviously also dependent on the caller.
Is it possible to get the projects root path independent of the calling function?
Or is my code design wrong? Should I pass the file path into the function?
Starting from Go 1.16, you can use the embed package. This allows you to embed the files in the running go program. It comes with the caveat that the referenced directory needs to exist at or below the embedding file. In your case, the structure would look as follows:
-- main.go
-- cleanup
-- wordlist
\- specialChars.txt
CleanupUtil.go
CleanupUtil_test.go
You can reference the file using a go directive
// CleanupUtil.go
package cleanup
import (
"embed"
)
//go:embed wordlists/specialChars.txt
var content embed.FS
func RemoveSpecialChars(word string) string {
file, err := content.ReadFile("wordlists/specialChars.txt")
[...]
}
This program will run successfully regardless of where the program is executed. You should be able to reference this code in both your main.go file and your CleanupUtil_test.go file.
Pass in the filepath as a parameter to the function (as indicated in your last question).
More details:
The relative path "wordlists/specialChars.txt" is in fact not dependent on where the source file is located (such as Main.go or CleanupUtil_test.go), but where you execute it from. So you could run your tests from your root directory and then it would actually work. In short, the current working directory is relevant.
Still, I'd recommend specifying the path, because that makes your function more reusable and universal.
Maybe you don't even need to put this information into a file, but can simply have a string containing those chars. In this case you could also check if https://golang.org/pkg/regexp/#Regexp.ReplaceAll already covers your use case.

File path in golang

I have a project with next structure:
|_main.go
|_config
|_config.go
|_config_test.go
|_config.json
I'm having next code line in config.go:
file, _ := os.Open("config/config.json")
When I'm executing method contained this code line from main.go all is working. But when I'm trying to execute this method from config_test.go it produces error:
open config/config.json: no such file or directory
As I understood it is a working directory issue because I'm launching same code with relative path from different directories. How can I fix this problem without using full path in config.go?
Relative paths are always resolved basis your current directory. Hence it's better to avoid relative paths.
Use command line flags or a configuration management tool (better approach) like Viper
Also according to The Twelve-Factor App your config files should be outside your project.
Eg usage with Viper:
import "github.com/spf13/viper"
func init() {
viper.SetConfigName("config")
// Config files are stored here; multiple locations can be added
viper.AddConfigPath("$HOME/configs")
errViper := viper.ReadInConfig()
if errViper != nil {
panic(errViper)
}
// Get values from config.json
val := viper.GetString("some_key")
// Use the value
}

Specifying template filenames for template.ParseFiles

My current directory structure looks like the following:
App
- Template
- foo.go
- foo.tmpl
- Model
- bar.go
- Another
- Directory
- baz.go
The file foo.go uses ParseFiles to read in the template file during init.
import "text/template"
var qTemplate *template.Template
func init() {
qTemplate = template.Must(template.New("temp").ParseFiles("foo.tmpl"))
}
...
Unit tests for foo.go work as expected. However, I am now trying to run unit tests for bar.go and baz.go which both import foo.go and I get a panic on trying to open foo.tmpl.
/App/Model$ go test
panic: open foo.tmpl: no such file or directory
/App/Another/Directory$ go test
panic: open foo.tmpl: no such file or directory
I've tried specifying the template name as a relative directory ("./foo.tmpl"), a full directory ("~/go/src/github.com/App/Template/foo.tmpl"), an App relative directory ("/App/Template/foo.tmpl"), and others but nothing seems to work for both cases. The unit tests fail for either bar.go or baz.go (or both).
Where should my template file be placed and how should I call ParseFiles so that it can always find the template file regardless of which directory I call go test from?
Helpful tip:
Use os.Getwd() and filepath.Join() to find the absolute path of a relative file path.
Example
// File: showPath.go
package main
import (
"fmt"
"path/filepath"
"os"
)
func main(){
cwd, _ := os.Getwd()
fmt.Println( filepath.Join( cwd, "./template/index.gtpl" ) )
}
First off, I recommend that the template folder only contain templates for presentation and not go files.
Next, to make life easier, only run files from the root project directory. This will help make the path to an file consistent throughout go files nested within sub directories. Relative file paths start from where the current working directory, which is where the program was called from.
Example to show the change in current working directory
user#user:~/go/src/test$ go run showPath.go
/home/user/go/src/test/template/index.gtpl
user#user:~/go/src/test$ cd newFolder/
user#user:~/go/src/test/newFolder$ go run ../showPath.go
/home/user/go/src/test/newFolder/template/index.gtpl
As for test files, you can run individual test files by supplying the file name.
go test foo/foo_test.go
Lastly, use a base path and the path/filepath package to form file paths.
Example:
var (
basePath = "./public"
templatePath = filepath.Join(basePath, "template")
indexFile = filepath.Join(templatePath, "index.gtpl")
)

Resources