11package configuration
22
33import (
4- "encoding/json"
5- "errors"
64 "fmt"
7- "github.com/captainhook-go/captainhook/info"
8- "github.com/captainhook-go/captainhook/io"
95 "os"
10- "path/filepath"
116)
127
13- type Factory struct {
14- includeLevel int
15- maxIncludeLevel int
8+ type ConfigFactory interface {
9+ // CreateConfig creates a CaptainHook configuration
10+ CreateConfig ( path string , cliSettings * NullableAppSettings ) ( * Configuration , error )
1611}
1712
18- // CreateConfig creates a default configuration in case the file exists it is loaded
19- func (f * Factory ) CreateConfig (path string , cliSettings * JsonAppSettings ) (* Configuration , error ) {
20- c , err := f .setupConfig (path )
21- // load the local config "captainhook.config.json"
22- decodeErr := f .loadSettingsFile (c )
23- if decodeErr != nil {
24- return c , decodeErr
25- }
26- // everything provided from the command line should overwrite any loaded configuration
27- // this works even if there is an error because then you have a default configuration
28- c .overwriteSettings (cliSettings )
29- return c , err
30- }
31-
32- // setupConfig creates a new configuration and loads the json file if it exists
33- func (f * Factory ) setupConfig (path string ) (* Configuration , error ) {
34- var err error
35- c := NewConfiguration (path , io .FileExists (path ))
36- if c .fileExists {
37- err = f .loadFromFile (c )
38- }
39- return c , err
40- }
41-
42- func (f * Factory ) loadFromFile (c * Configuration ) error {
43- jsonBytes , readError := f .readConfigFile (c .path )
44- if readError != nil {
45- return readError
46- }
47- configurationJson , decodeErr := f .decodeConfigJson (jsonBytes )
48- if decodeErr != nil {
49- return fmt .Errorf ("unable to parse json: %s %s" , c .path , decodeErr .Error ())
50- }
51- c .overwriteSettings (configurationJson .Settings )
52-
53- if configurationJson .Hooks == nil {
54- return errors .New ("no hooks config found" )
55- }
56- includeErr := f .appendIncludedConfiguration (c )
57- if includeErr != nil {
58- return includeErr
59- }
60-
61- for hookName , hookConfigJson := range * configurationJson .Hooks {
62- hookConfig := c .HookConfig (hookName )
63- hookConfig .isEnabled = true
64- for _ , actionJson := range hookConfigJson .Actions {
65- if ! f .isValidAction (actionJson ) {
66- return fmt .Errorf ("invalid action config in %s" , hookName )
67- }
68- hookConfig .AddAction (CreateActionFromJson (actionJson ))
69- }
70- }
71- return nil
72- }
73-
74- func (f * Factory ) loadSettingsFile (c * Configuration ) error {
75- directory := filepath .Dir (c .path )
76- filePath := directory + "/captainhook.config.json"
77-
78- // no local config file to load just exit
79- if ! io .FileExists (filePath ) {
80- return nil
81- }
82-
83- jsonBytes , readError := f .readConfigFile (filePath )
84- if readError != nil {
85- return readError
86- }
87- appSettingJson , decodeErr := f .decodeSettingJson (jsonBytes )
88- if decodeErr != nil {
89- return fmt .Errorf ("unable to parse json: %s %s" , filePath , decodeErr .Error ())
90- }
91- // overwrite current settings
92- c .overwriteSettings (appSettingJson )
93- return nil
94- }
95-
96- func (f * Factory ) appendIncludedConfiguration (c * Configuration ) error {
97- f .detectMaxIncludeLevel (c )
98- if f .includeLevel < f .maxIncludeLevel {
99- f .includeLevel ++
100- includes , err := f .loadIncludedConfigs (c .Includes (), c .path )
101- if err != nil {
102- return err
103- }
104- for _ , configToInclude := range includes {
105- f .mergeHookConfigs (configToInclude , c )
106- }
107- f .includeLevel --
108- }
109- return nil
110- }
111-
112- func (f * Factory ) mergeHookConfigs (from , to * Configuration ) {
113- for _ , hook := range info .GetValidHooks () {
114- // This `Enable` is solely to overwrite the main configuration in the special case that the hook
115- // is not configured at all. In this case the empty config is disabled by default, and adding an
116- // empty hook config just to enable the included actions feels a bit dull.
117- // Since the main hook is processed last (if one is configured) the enabled flag will be overwritten
118- // once again by the main config value. This is to make sure that if somebody disables a hook in its
119- // main configuration no actions will get executed, even if we have enabled hooks in any include file.
120- targetHookConfig := to .HookConfig (hook )
121- targetHookConfig .Enable ()
122- f .copyActionsFromTo (from .HookConfig (hook ), targetHookConfig )
123- }
124- }
125-
126- func (f * Factory ) readConfigFile (path string ) ([]byte , error ) {
13+ func readConfigFile (path string ) ([]byte , error ) {
12714 fileInfo , err := os .Stat (path )
12815 if err != nil {
12916 return nil , err
@@ -138,76 +25,8 @@ func (f *Factory) readConfigFile(path string) ([]byte, error) {
13825 return jsonData , nil
13926}
14027
141- func (f * Factory ) decodeConfigJson (jsonInBytes []byte ) (JsonConfiguration , error ) {
142- var jConfig JsonConfiguration
143- if ! json .Valid (jsonInBytes ) {
144- return jConfig , fmt .Errorf ("json configuration is invalid" )
145- }
146- marshalError := json .Unmarshal (jsonInBytes , & jConfig )
147- if marshalError != nil {
148- return jConfig , fmt .Errorf ("could not load json to struct: %s" , marshalError .Error ())
149- }
150- return jConfig , nil
151- }
152-
153- func (f * Factory ) decodeSettingJson (jsonInBytes []byte ) (* JsonAppSettings , error ) {
154- var jSettings JsonAppSettings
155- if ! json .Valid (jsonInBytes ) {
156- return nil , fmt .Errorf ("json configuration is invalid" )
157- }
158- marshalError := json .Unmarshal (jsonInBytes , & jSettings )
159- if marshalError != nil {
160- return nil , fmt .Errorf ("could not load json to struct: %s" , marshalError .Error ())
161- }
162- return & jSettings , nil
163- }
164-
165- func (f * Factory ) detectMaxIncludeLevel (c * Configuration ) {
166- // read the include-level setting only for the actual configuration not any included ones
167- if f .includeLevel == 0 {
168- f .maxIncludeLevel = c .MaxIncludeLevel ()
169- }
170- }
171-
172- func (f * Factory ) loadIncludedConfigs (includes []string , path string ) ([]* Configuration , error ) {
173- var configs []* Configuration
174- directory := filepath .Dir (path )
175-
176- for _ , file := range includes {
177- config , err := f .includeConfig (directory + "/" + file )
178- if err != nil {
179- return nil , err
180- }
181- configs = append (configs , config )
182- }
183- return configs , nil
184- }
185-
186- func (f * Factory ) includeConfig (path string ) (* Configuration , error ) {
187- if ! io .FileExists (path ) {
188- return nil , fmt .Errorf ("config to include not found: %s" , path )
189- }
190- return f .setupConfig (path )
191- }
192-
193- func (f * Factory ) copyActionsFromTo (from * Hook , to * Hook ) {
28+ func copyActionsFromTo (from * Hook , to * Hook ) {
19429 for _ , action := range from .GetActions () {
19530 to .AddAction (action )
19631 }
19732}
198-
199- func (f * Factory ) isValidAction (actionJson * JsonAction ) bool {
200- if len (actionJson .Run ) == 0 {
201- return false
202- }
203- for _ , condition := range actionJson .Conditions {
204- if len (condition .Run ) == 0 {
205- return false
206- }
207- }
208- return true
209- }
210-
211- func NewFactory () * Factory {
212- return & Factory {includeLevel : 0 }
213- }
0 commit comments