Skip to content

Commit 9f746c0

Browse files
authored
Merge pull request #6 from cmuench/feature/extension-based-config
New more flexible configuration format [BREAKING CHANGE]
2 parents 40f9680 + 1322817 commit 9f746c0

File tree

8 files changed

+259
-152
lines changed

8 files changed

+259
-152
lines changed

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# inotify-proxy
22

33
This tools helps to detect changed files in Docker containers.
4-
If a file is changed from hostsystem a file watcher inside the container detects the change
5-
and triggers a inotify event.
4+
If a file is changed from host system a file watcher inside the container detects the change
5+
and triggers an inotify event.
66

77
## Purpose
88

@@ -37,12 +37,17 @@ Example config:
3737

3838
---
3939
watch:
40-
- dir: /tmp/watch1
41-
- dir: /tmp/watch2
42-
profile: magento2
43-
40+
- directory: /tmp/watch1
41+
profile: magento2
42+
43+
- dir: /tmp/watch2
44+
profile: sass
45+
46+
- dir: /tmp/watch3
47+
extensions: [.css, .html]
48+
4449
The profile setting is optional.
45-
The config loading can be skiped by adding the option `-no-config`.
50+
The config loading can be skipped by adding the option `-no-config`.
4651

4752
## Supported Profiles
4853

inotify-proxy.go

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,38 +25,64 @@ func main() {
2525
c := config.Config{}
2626

2727
if !*noConfig {
28-
includedDirectories = loadConfig(c, includedDirectories, profilePtr)
29-
}
28+
if util.FileExists("inotify-proxy.yaml") {
3029

31-
// If no argument is defined, the current directory is used
32-
if len(includedDirectories) == 0 {
33-
includedDirectories = append(includedDirectories, ".")
34-
}
30+
r, err := os.Open("inotify-proxy.yaml")
3531

36-
color.Style{color.FgCyan, color.OpBold}.Println("PROFILE: " + *profilePtr)
37-
color.Style{color.FgCyan, color.OpBold}.Println("DIRECTORIES: " + strings.Join(includedDirectories, ","))
32+
if err != nil {
33+
color.Errorf("cannot read file: %v\n", err)
34+
os.Exit(1)
35+
}
3836

39-
watcher.Watch(includedDirectories, *sleepPtr, *profilePtr)
40-
}
37+
defer r.Close()
4138

42-
func loadConfig(c config.Config, includedDirectories []string, profilePtr *string) []string {
43-
if util.FileExists("inotify-proxy.yaml") {
44-
color.Info.Println("load config")
45-
c, err := config.ReadFile("inotify-proxy.yaml");
39+
c, err = config.Read(r)
4640

47-
if err != nil {
48-
color.Errorf("error: Invalid config provided.\n")
49-
os.Exit(1)
41+
if err != nil {
42+
color.Errorf("cannot read config: %v\n", err)
43+
}
44+
45+
if c.OldGlobalProfile != nil {
46+
color.Errorf("You are using the old configuration format. Please use the new configuration version.\n")
47+
color.Print("\nPlease refer: https://github.com/cmuench/inotify-proxy/blob/master/README.md#config\n")
48+
os.Exit(1)
49+
}
5050
}
51+
}
5152

52-
for _, watch := range c.Watch {
53-
includedDirectories = append(includedDirectories, watch.Dir)
53+
if len(includedDirectories) > 0 {
54+
for _, includedDirectory := range includedDirectories {
55+
c.Entries = append(c.Entries, config.WatchEntry{
56+
Directory: includedDirectory,
57+
Extensions: nil,
58+
Profile: profilePtr,
59+
})
5460
}
61+
}
5562

56-
if c.Profile != "" {
57-
*profilePtr = c.Profile
63+
// If no argument is defined, the current directory is used
64+
if len(c.Entries) == 0 {
65+
c.AddEntry(config.WatchEntry{
66+
Directory: ".",
67+
Extensions: nil,
68+
Profile: profilePtr,
69+
})
70+
}
71+
72+
color.Style{color.FgMagenta, color.OpBold}.Println("Watching ...")
73+
color.Style{color.FgWhite}.Println(strings.Repeat("-", 80))
74+
75+
for _, e := range c.Entries {
76+
color.Style{color.FgCyan, color.OpBold}.Printf("Directory: %s\n", e.Directory)
77+
if *e.Profile != "" {
78+
color.Style{color.FgCyan, color.OpBold}.Printf("Profile: %s\n", *e.Profile)
79+
}
80+
if len(e.Extensions) > 0 {
81+
color.Style{color.FgCyan, color.OpBold}.Printf("Extensions: %s\n", e.Extensions)
5882
}
83+
84+
color.Style{color.FgWhite}.Println(strings.Repeat("-", 80))
5985
}
6086

61-
return includedDirectories
87+
watcher.Watch(c, *sleepPtr)
6288
}

internal/config/config.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,43 @@ package config
22

33
import (
44
"gopkg.in/yaml.v3"
5+
"io"
56
"io/ioutil"
67
)
78

8-
type Watch struct {
9-
Dir string `yaml:"dir"`
9+
type Config struct {
10+
Entries []WatchEntry `yaml:"watch"`
11+
12+
OldGlobalProfile *string `yaml:"profile"`
1013
}
1114

12-
type Config struct {
13-
Watch []Watch `yaml:"watch"`
14-
Profile string `yaml:"profile"`
15+
type WatchEntry struct {
16+
Directory string `yaml:"directory"`
17+
Extensions []string `yaml:"extensions"`
18+
Profile *string `yaml:"profile"`
19+
}
20+
21+
func (c *Config) AddEntry(e WatchEntry) {
22+
c.Entries = append(c.Entries, e)
23+
}
24+
25+
func (c *Config) GetEntryByDirectory(dir string) WatchEntry {
26+
for _, e := range c.Entries {
27+
if e.Directory == dir {
28+
return e
29+
}
30+
}
31+
32+
return WatchEntry{}
1533
}
1634

17-
func ReadFile(filename string) (Config, error) {
35+
func Read(f io.Reader) (Config, error) {
1836
var (
19-
c Config
20-
err error
37+
c Config
38+
err error
2139
yamlData []byte
2240
)
23-
yamlData, err = ioutil.ReadFile(filename)
41+
yamlData, err = ioutil.ReadAll(f)
2442

2543
if err != nil {
2644
return c, err

internal/config/config_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,28 @@ func TestParseValidYaml(t *testing.T) {
1010
validYamlData := `
1111
---
1212
watch:
13-
- dir: /tmp/watch1
14-
- dir: /tmp/watch2
15-
profile: magento2
13+
- directory: /tmp/watch1
14+
extensions:
15+
- ".scss"
16+
- ".js"
17+
- ".twig"
18+
19+
- directory: /tmp/watch2
20+
profile: magento2
21+
extensions:
22+
- ".scss"
23+
- ".js"
24+
- ".twig"
1625
1726
`
1827
c, err := Parse([]byte(validYamlData))
1928

2029
assert.NoError(t, err, "Config is valid and should not throw an error")
2130
assert.IsType(t, Config{}, c)
31+
32+
assert.Equal(t, "/tmp/watch1", c.Entries[0].Directory)
33+
assert.Equal(t, "/tmp/watch2", c.Entries[1].Directory)
34+
2235
}
2336

2437
func TestParseInvalidYaml(t *testing.T) {
@@ -31,3 +44,7 @@ watch
3144

3245
assert.Error(t, err, "Config is invalid and should throw an error")
3346
}
47+
48+
func TestLoad(t *testing.T) {
49+
50+
}

internal/profile/types.go

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,33 @@
11
package profile
22

3-
import (
4-
"path/filepath"
5-
"strings"
6-
)
7-
83
type Profile struct {
9-
fileExtensions []string
10-
}
11-
12-
func (l *Profile) IsAllowedFileExtension(path string) bool {
13-
14-
// if profile contains only one extension definition with "*" then allow every extension.
15-
if len(l.fileExtensions) == 1 && l.fileExtensions[0] == "*" {
16-
return true
17-
}
18-
19-
extension := filepath.Ext(path)
20-
21-
for _, a := range l.fileExtensions {
22-
if a == extension {
23-
return true
24-
}
25-
}
26-
27-
return false
28-
}
29-
30-
func (l *Profile) IsAllowedDirectory(path string) bool {
31-
// Exclude some directories by default
32-
excludedDirectories := [...]string{
33-
"node_modules/",
34-
".idea/",
35-
".git/",
36-
".svn/",
37-
}
38-
39-
for _, excludedDirectory := range excludedDirectories {
40-
if strings.Contains(path, excludedDirectory) {
41-
return false
42-
}
43-
}
44-
45-
return true
4+
Extensions []string
465
}
476

487
var Default = Profile{
49-
fileExtensions: []string{"*"},
8+
Extensions: []string{"*"},
509
}
5110

5211
var LESS = Profile{
53-
fileExtensions: []string{".less"},
12+
Extensions: []string{".less"},
5413
}
5514

5615
var Magento2Theme = Profile{
57-
fileExtensions: []string{".css", ".js", ".less", ".sass", ".ts"},
16+
Extensions: []string{".css", ".js", ".less", ".sass", ".ts"},
5817
}
5918

6019
var Magento2 = Profile{
61-
fileExtensions: []string{".css", ".html", ".less", ".sass", ".js", ".php", ".phtml", ".ts", ".xml"},
20+
Extensions: []string{".css", ".html", ".less", ".sass", ".js", ".php", ".phtml", ".ts", ".xml"},
6221
}
6322

6423
var SASS = Profile{
65-
fileExtensions: []string{".sass", ".scss"},
24+
Extensions: []string{".sass", ".scss"},
6625
}
6726

6827
var VueStorefront = Profile{
69-
fileExtensions: []string{".css", ".js", ".sass", ".ts"},
28+
Extensions: []string{".css", ".js", ".sass", ".ts"},
7029
}
7130

7231
var Javascript = Profile{
73-
fileExtensions: []string{".js", ".ts"},
32+
Extensions: []string{".js", ".ts"},
7433
}

internal/profile/validator/path.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
package validator
22

33
import (
4+
"github.com/cmuench/inotify-proxy/internal/config"
45
"github.com/cmuench/inotify-proxy/internal/profile"
6+
"path/filepath"
7+
"strings"
58
)
69

7-
func IsPathValid(path string, profileName string) bool {
10+
func IsPathValid(path string, entryConfig config.WatchEntry) bool {
11+
12+
if !isAllowedDirectory(path) {
13+
return false
14+
}
15+
16+
if len(entryConfig.Extensions) > 0 && !isAllowedFileExtension(path, entryConfig.Extensions) {
17+
return false
18+
}
19+
20+
if entryConfig.Profile == nil {
21+
return true
22+
}
823

924
var selectedProfile profile.Profile
1025

11-
switch profileName {
26+
switch *entryConfig.Profile {
1227
case "less":
1328
selectedProfile = profile.LESS
1429
case "magento2":
@@ -25,5 +40,41 @@ func IsPathValid(path string, profileName string) bool {
2540
selectedProfile = profile.Default
2641
}
2742

28-
return selectedProfile.IsAllowedDirectory(path) && selectedProfile.IsAllowedFileExtension(path)
43+
return isAllowedFileExtension(path, selectedProfile.Extensions)
44+
}
45+
46+
func isAllowedDirectory(path string) bool {
47+
// Exclude some directories by default
48+
excludedDirectories := [...]string{
49+
"node_modules/",
50+
".idea/",
51+
".git/",
52+
".svn/",
53+
}
54+
55+
for _, excludedDirectory := range excludedDirectories {
56+
if strings.Contains(path, excludedDirectory) {
57+
return false
58+
}
59+
}
60+
61+
return true
62+
}
63+
64+
func isAllowedFileExtension(path string, fileExtensions []string) bool {
65+
66+
// if profile contains only one extension definition with "*" then allow every extension.
67+
if len(fileExtensions) == 1 && fileExtensions[0] == "*" {
68+
return true
69+
}
70+
71+
extension := filepath.Ext(path)
72+
73+
for _, a := range fileExtensions {
74+
if a == extension {
75+
return true
76+
}
77+
}
78+
79+
return false
2980
}

0 commit comments

Comments
 (0)