Skip to content

Commit 00b1bec

Browse files
committed
feat(cmd): support multiple configuration files
- this should hopefully help to reduce copy-pasting for users - I just changed 2 example test to test this feature - the difference is order of the configuration files, for coreboot the target is in first config, while for linux it is in the second Signed-off-by: AtomicFS <vojtech.vesely@9elements.com>
1 parent 9ef4ae6 commit 00b1bec

File tree

3 files changed

+108
-9
lines changed

3 files changed

+108
-9
lines changed

.github/workflows/example.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,15 @@ jobs:
156156
uses: ./
157157
# uses: 9elements/firmware-action
158158
with:
159-
config: 'tests/example_config__coreboot.json'
159+
config: |-
160+
tests/example_config__coreboot.json
161+
tests/example_config__uroot.json
160162
target: 'coreboot-example'
161163
recursive: 'false'
162164
compile: ${{ needs.changes.outputs.compile }}
163165
env:
164166
COREBOOT_VERSION: ${{ matrix.coreboot-version }}
167+
UROOT_VERSION: "dummy"
165168

166169
- name: Get artifacts
167170
uses: actions/upload-artifact@v4
@@ -229,13 +232,16 @@ jobs:
229232
uses: ./
230233
# uses: 9elements/firmware-action
231234
with:
232-
config: 'tests/example_config__linux.json'
235+
config: |-
236+
tests/example_config__uroot.json
237+
tests/example_config__linux.json
233238
target: 'linux-example'
234239
recursive: 'false'
235240
compile: ${{ needs.changes.outputs.compile }}
236241
env:
237242
LINUX_VERSION: ${{ matrix.linux-version }}
238243
SYSTEM_ARCH: ${{ matrix.arch }}
244+
UROOT_VERSION: "dummy"
239245

240246
- name: Get artifacts
241247
uses: actions/upload-artifact@v4

cmd/firmware-action/main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"log/slog"
1313
"os"
1414
"regexp"
15+
"strings"
1516

1617
"github.com/9elements/firmware-action/cmd/firmware-action/filesystem"
1718
"github.com/9elements/firmware-action/cmd/firmware-action/logging"
@@ -42,7 +43,7 @@ var CLI struct {
4243
Indent bool `default:"false" help:"enable indentation for JSON output"`
4344
Debug bool `default:"false" help:"increase verbosity"`
4445

45-
Config string `type:"path" required:"" default:"${config_file}" help:"Path to configuration file"`
46+
Config []string `type:"path" required:"" default:"${config_file}" help:"Path to configuration file, supports multiple flags to use multiple configuration files"`
4647

4748
Build struct {
4849
Target string `required:"" help:"Select which target to build, use ID from configuration file"`
@@ -76,7 +77,7 @@ func run(ctx context.Context) error {
7677
)
7778
slog.Info(
7879
fmt.Sprintf("Running in %s mode", mode),
79-
slog.String("input/config", CLI.Config),
80+
slog.Any("input/config", CLI.Config),
8081
slog.String("input/target", CLI.Build.Target),
8182
slog.Bool("input/recursive", CLI.Build.Recursive),
8283
)
@@ -108,7 +109,7 @@ func run(ctx context.Context) error {
108109
patterSub := regexp.MustCompile(`^\-[\d\w]* `)
109110
slog.Warn(
110111
"Git submodule seems to be uninitialized",
111-
slog.String("suggestion", "run 'git submodule update --depth 0 --init --recursive --checkout'"),
112+
slog.String("suggestion", "run 'git submodule update --depth 1 --init --recursive --checkout'"),
112113
slog.String("offending_submodule", patterSub.ReplaceAllString(v, "")),
113114
)
114115
}
@@ -117,7 +118,7 @@ submodule_out:
117118

118119
// Parse configuration file
119120
var myConfig *recipes.Config
120-
myConfig, err = recipes.ReadConfig(CLI.Config)
121+
myConfig, err = recipes.ReadConfigs(CLI.Config)
121122
if err != nil {
122123
return err
123124
}
@@ -189,7 +190,7 @@ func parseCli() (string, error) {
189190

190191
case "generate-config":
191192
// Check if config file exists
192-
err := filesystem.CheckFileExists(CLI.Config)
193+
err := filesystem.CheckFileExists(CLI.Config[0])
193194
if !errors.Is(err, os.ErrNotExist) {
194195
// The file exists, or is directory, or some other problem
195196
slog.Error(
@@ -222,7 +223,7 @@ func parseCli() (string, error) {
222223

223224
// Write to file
224225
slog.Info(fmt.Sprintf("Generating configuration file at: %s", CLI.Config))
225-
if err := os.WriteFile(CLI.Config, jsonString, 0o666); err != nil {
226+
if err := os.WriteFile(CLI.Config[0], jsonString, 0o666); err != nil {
226227
slog.Error(
227228
"Unable to write generated configuration into file",
228229
slog.Any("error", err),
@@ -253,7 +254,7 @@ func parseGithub() (string, error) {
253254
action := githubactions.New()
254255
regexTrue := regexp.MustCompile(`(?i)true`)
255256

256-
CLI.Config = action.GetInput("config")
257+
CLI.Config = strings.Split(action.GetInput("config"), "\n")
257258
CLI.Build.Target = action.GetInput("target")
258259
CLI.Build.Recursive = regexTrue.MatchString(action.GetInput("recursive"))
259260
CLI.JSON = regexTrue.MatchString(action.GetInput("json"))

cmd/firmware-action/recipes/config.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,78 @@ func (c Config) AllModules() map[string]FirmwareModule {
230230
return modules
231231
}
232232

233+
func checkIfModuleExists(ok bool, key string) {
234+
if ok {
235+
slog.Warn("Module already exists in configuration, and will be overwritten",
236+
slog.String("module_name", key),
237+
slog.String("suggestion", "Make sure each module has unique name, not only per single configuration file but across all configuration files."),
238+
)
239+
}
240+
}
241+
242+
// Merge method will take other Config instance and adopt all of its modules
243+
func (c Config) Merge(other Config) (Config, error) {
244+
// Not sure of there is cleaner way to do this :/
245+
246+
for key, value := range other.Coreboot {
247+
if len(c.Coreboot) == 0 {
248+
c.Coreboot = map[string]CorebootOpts{}
249+
}
250+
_, ok := c.Coreboot[key]
251+
checkIfModuleExists(ok, key)
252+
c.Coreboot[key] = value
253+
}
254+
for key, value := range other.Linux {
255+
if len(c.Linux) == 0 {
256+
c.Linux = map[string]LinuxOpts{}
257+
}
258+
_, ok := c.Linux[key]
259+
checkIfModuleExists(ok, key)
260+
c.Linux[key] = value
261+
}
262+
for key, value := range other.Edk2 {
263+
if len(c.Edk2) == 0 {
264+
c.Edk2 = map[string]Edk2Opts{}
265+
}
266+
_, ok := c.Edk2[key]
267+
checkIfModuleExists(ok, key)
268+
c.Edk2[key] = value
269+
}
270+
for key, value := range other.FirmwareStitching {
271+
if len(c.FirmwareStitching) == 0 {
272+
c.FirmwareStitching = map[string]FirmwareStitchingOpts{}
273+
}
274+
_, ok := c.FirmwareStitching[key]
275+
checkIfModuleExists(ok, key)
276+
c.FirmwareStitching[key] = value
277+
}
278+
for key, value := range other.URoot {
279+
if len(c.URoot) == 0 {
280+
c.URoot = map[string]URootOpts{}
281+
}
282+
_, ok := c.URoot[key]
283+
checkIfModuleExists(ok, key)
284+
c.URoot[key] = value
285+
}
286+
for key, value := range other.Universal {
287+
if len(c.Universal) == 0 {
288+
c.Universal = map[string]UniversalOpts{}
289+
}
290+
_, ok := c.Universal[key]
291+
checkIfModuleExists(ok, key)
292+
c.Universal[key] = value
293+
}
294+
for key, value := range other.UBoot {
295+
if len(c.UBoot) == 0 {
296+
c.UBoot = map[string]UBootOpts{}
297+
}
298+
_, ok := c.UBoot[key]
299+
checkIfModuleExists(ok, key)
300+
c.UBoot[key] = value
301+
}
302+
return c, nil
303+
}
304+
233305
// FirmwareModule interface
234306
type FirmwareModule interface {
235307
GetDepends() []string
@@ -273,6 +345,26 @@ func FindAllEnvVars(text string) []string {
273345
return result
274346
}
275347

348+
// ReadConfigs is for reading and parsing multiple JSON configuration files into single Config struct
349+
func ReadConfigs(filepaths []string) (*Config, error) {
350+
var allConfigs Config
351+
for _, filepath := range filepaths {
352+
trimmedFilepath := strings.TrimSpace(filepath)
353+
slog.Debug("Reading config",
354+
slog.String("path", trimmedFilepath),
355+
)
356+
payload, err := ReadConfig(trimmedFilepath)
357+
if err != nil {
358+
return nil, err
359+
}
360+
allConfigs, err = allConfigs.Merge(*payload)
361+
if err != nil {
362+
return nil, err
363+
}
364+
}
365+
return &allConfigs, nil
366+
}
367+
276368
// ReadConfig is for reading and parsing JSON configuration file into Config struct
277369
func ReadConfig(filepath string) (*Config, error) {
278370
// Read JSON file

0 commit comments

Comments
 (0)