From 50ab2ff7e4a36041e1a9dfa0bdb5a3df06bd4afc Mon Sep 17 00:00:00 2001 From: Timothy Middelkoop Date: Tue, 25 Feb 2025 02:57:36 +0000 Subject: [PATCH 1/7] Install git before checkout Signed-off-by: Timothy Middelkoop --- .github/workflows/nightly-release.yml | 3 +++ CHANGELOG.md | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/nightly-release.yml b/.github/workflows/nightly-release.yml index 989908e2..f413c29c 100644 --- a/.github/workflows/nightly-release.yml +++ b/.github/workflows/nightly-release.yml @@ -51,6 +51,9 @@ jobs: permissions: write-all steps: + - name: Install git + run: | + dnf install -y git - name: Checkout Code uses: actions/checkout@v4 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index b95d99e1..93b7700a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## v4.6.0, unreleased + +### Fixed + +- Fix nightly builds. + ## v4.6.0rc3, 2025-02-23 ### Added From 3a1e581f9313398ed83793232b60ca70f92570cb Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Mon, 24 Feb 2025 22:24:13 -0700 Subject: [PATCH 2/7] Better handling for missing NetDev.Device - Closes: #1777 Overlays: - ifcfg - NetworkManager Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 1 + .../internal/networkmanager_test.go | 72 ++++++++++++++++++- .../NetworkManager/conf.d/ww4-unmanaged.ww | 13 +++- .../system-connections/ww4-managed.ww | 4 ++ overlays/ifcfg/internal/ifcfg_test.go | 68 ++++++++++++++++++ .../etc/sysconfig/network-scripts/ifcfg.ww | 2 + 6 files changed, 157 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b7700a..576f170b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Fix nightly builds. +- Better handling for missing NetDev.Device. #1777 ## v4.6.0rc3, 2025-02-23 diff --git a/overlays/NetworkManager/internal/networkmanager_test.go b/overlays/NetworkManager/internal/networkmanager_test.go index 3d106194..f9db63e0 100644 --- a/overlays/NetworkManager/internal/networkmanager_test.go +++ b/overlays/NetworkManager/internal/networkmanager_test.go @@ -50,7 +50,7 @@ Filename: warewulf-unmanaged.conf plugins=keyfile [keyfile] -unmanaged-devices=except:mac:e6:92:39:49:7b:03,except:interface-name:wwnet0,except:mac:9a:77:29:73:14:f1,except:interface-name:wwnet1, +unmanaged-devices=except:mac:e6:92:39:49:7b:03,except:interface-name:wwnet0,except:mac:9a:77:29:73:14:f1,except:interface-name:wwnet1 `, }, "NetworkManager:ww4-managed.ww": { @@ -119,6 +119,74 @@ gateway=192.168.3.1 dns=8.8.8.8;8.8.4.4; dns-search=example.com;example.net; +[ipv6] +addr-gen-mode=stable-privacy +method=ignore +never-default=true +`, + }, + "NetworkManager:ww4-managed.ww without device": { + nodes_conf: ` +nodes: + node1: + network devices: + default: + onboot: true + hwaddr: e6:92:39:49:7b:03 + ipaddr: 192.168.3.21 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + secondary: + onboot: true + hwaddr: 9a:77:29:73:14:f1 + ipaddr: 192.168.3.22 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + tags: + DNS1: 8.8.8.8 + DNS2: 8.8.4.4 + DNSSEARCH: "example.com;example.net;" +`, + args: []string{"--render", "node1", "NetworkManager", "etc/NetworkManager/system-connections/ww4-managed.ww"}, + log: `backupFile: true +writeFile: true +Filename: warewulf-default.conf + +# This file is autogenerated by warewulf + +[connection] +id=default +type=ethernet +autoconnect=true +[ethernet] +mac-address=e6:92:39:49:7b:03 +[ipv4] +method=manual +address=192.168.3.21/24 +gateway=192.168.3.1 + +[ipv6] +addr-gen-mode=stable-privacy +method=ignore +never-default=true +backupFile: true +writeFile: true +Filename: warewulf-secondary.conf +# This file is autogenerated by warewulf + +[connection] +id=secondary +type=ethernet +autoconnect=true +[ethernet] +mac-address=9a:77:29:73:14:f1 +[ipv4] +method=manual +address=192.168.3.22/24 +gateway=192.168.3.1 +dns=8.8.8.8;8.8.4.4; +dns-search=example.com;example.net; + [ipv6] addr-gen-mode=stable-privacy method=ignore @@ -158,7 +226,7 @@ Filename: warewulf-unmanaged.conf plugins=keyfile [keyfile] -unmanaged-devices=except:interface-name:wwnet0,except:mac:9a:77:29:73:14:f1,except:interface-name:wwnet1, +unmanaged-devices=except:interface-name:wwnet0,except:mac:9a:77:29:73:14:f1,except:interface-name:wwnet1 `, }, "NetworkManager:ww4-managed.ww with vlan": { diff --git a/overlays/NetworkManager/rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww b/overlays/NetworkManager/rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww index 1afa9491..4b66b892 100644 --- a/overlays/NetworkManager/rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww +++ b/overlays/NetworkManager/rootfs/etc/NetworkManager/conf.d/ww4-unmanaged.ww @@ -5,4 +5,15 @@ plugins=keyfile [keyfile] -unmanaged-devices={{ range $connection_id, $netdev := .NetDevs }}{{ if $netdev.Hwaddr }}except:mac:{{ $netdev.Hwaddr }},{{ end }}{{ if $netdev.Device }}except:interface-name:{{ $netdev.Device }},{{ end }}{{ end }} +{{- $unmanaged := list }} +{{- range $_, $netdev := .NetDevs }} +{{- if $netdev.Hwaddr }} +{{- $unmanaged = append $unmanaged (printf "except:mac:%s" $netdev.Hwaddr) }} +{{- end }} +{{- if $netdev.Device }} +{{- $unmanaged = append $unmanaged (printf "except:interface-name:%s" $netdev.Device) }} +{{- end }} +{{- end }} +{{- if $unmanaged }} +unmanaged-devices={{ join "," $unmanaged }} +{{- end }} diff --git a/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww b/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww index ae70f38b..2586837b 100644 --- a/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww +++ b/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww @@ -4,7 +4,9 @@ [connection] id={{ $devname }} +{{- if $netdev.Device }} interface-name={{ $netdev.Device }} +{{- end }} type={{ default "ethernet" (lower $netdev.Type) }} {{- if $netdev.Tags.master }} master={{ $netdev.Tags.master }} @@ -78,7 +80,9 @@ ipaddr="{{ $netdev.Ipaddr6 }}" {{- if eq $netdev.Type "vlan" }} [vlan] +{{- if $netdev.Device }} interface-name={{ $netdev.Device }} +{{- end }} parent={{ $netdev.Tags.parent_device }} id={{ $netdev.Tags.vlan_id }} {{- end }} diff --git a/overlays/ifcfg/internal/ifcfg_test.go b/overlays/ifcfg/internal/ifcfg_test.go index 654ec2d4..164349d9 100644 --- a/overlays/ifcfg/internal/ifcfg_test.go +++ b/overlays/ifcfg/internal/ifcfg_test.go @@ -86,6 +86,74 @@ IPV6_FAILURE_FATAL=no DNS1=8.8.8.8 DNS2=8.8.4.4 DOMAIN="example.net example.com" +`, + }, + "ifcfg:ifcfg.ww without device": { + nodes_conf: ` +nodes: + node1: + network devices: + default: + hwaddr: e6:92:39:49:7b:03 + ipaddr: 192.168.3.21 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + tags: + DNS1: 1.1.1.1 + DNS2: 1.0.0.1 + DNSSEARCH: "example.com example.net" + secondary: + hwaddr: 9a:77:29:73:14:f1 + ipaddr: 192.168.3.22 + netmask: 255.255.255.0 + gateway: 192.168.3.1 + tags: + DNS1: 8.8.8.8 + DNS2: 8.8.4.4 + DNSSEARCH: "example.net;example.com;" +`, + args: []string{"--render", "node1", "ifcfg", "etc/sysconfig/network-scripts/ifcfg.ww"}, + log: `backupFile: true +writeFile: true +Filename: ifcfg-default.conf + +# This file is autogenerated by warewulf +TYPE=Ethernet +NAME=default +BOOTPROTO=static +DEVTIMEOUT=10 +IPADDR=192.168.3.21 +NETMASK=255.255.255.0 +GATEWAY=192.168.3.1 +HWADDR=e6:92:39:49:7b:03 +ONBOOT=true +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +DNS1=1.1.1.1 +DNS2=1.0.0.1 +DOMAIN="example.com example.net" +backupFile: true +writeFile: true +Filename: ifcfg-secondary.conf +# This file is autogenerated by warewulf +TYPE=Ethernet +NAME=secondary +BOOTPROTO=static +DEVTIMEOUT=10 +IPADDR=192.168.3.22 +NETMASK=255.255.255.0 +GATEWAY=192.168.3.1 +HWADDR=9a:77:29:73:14:f1 +ONBOOT=true +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +DNS1=8.8.8.8 +DNS2=8.8.4.4 +DOMAIN="example.net example.com" `, }, "ifcfg:network.ww": { diff --git a/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww b/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww index 7086ff5e..f29a7bd0 100644 --- a/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww +++ b/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww @@ -9,7 +9,9 @@ TYPE={{ default "Ethernet" $netdev.Type }} {{- if $netdev.Tags.master }} MASTER={{ $netdev.Tags.master }} {{- end }} +{{- if $netdev.Device }} DEVICE={{ $netdev.Device }} +{{- end }} NAME={{ $devname }} {{- if $netdev.MTU }} MTU={{ $netdev.MTU }} From 8c65c681c9bbae6f5d3a47d67be7316198e69000 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 25 Feb 2025 01:17:24 -0700 Subject: [PATCH 3/7] Remove dracut NetworkManager connections for phase 2 - Closes: #1780 Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 1 + .../NetworkManager/rootfs/warewulf/init.d/20-NetworkManager | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 overlays/NetworkManager/rootfs/warewulf/init.d/20-NetworkManager diff --git a/CHANGELOG.md b/CHANGELOG.md index 576f170b..bc83d3af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix nightly builds. - Better handling for missing NetDev.Device. #1777 +- Remove dracut NetworkManager connections between boot phases. #1780 ## v4.6.0rc3, 2025-02-23 diff --git a/overlays/NetworkManager/rootfs/warewulf/init.d/20-NetworkManager b/overlays/NetworkManager/rootfs/warewulf/init.d/20-NetworkManager new file mode 100644 index 00000000..45481033 --- /dev/null +++ b/overlays/NetworkManager/rootfs/warewulf/init.d/20-NetworkManager @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "Warewulf prescript: NetworkManager" +echo +echo "Removing NetworkManager state from any prior boot stage..." +rm -rf /run/NetworkManager From 94b2c3ba845a1501aad9f577ae1211cd264c0f1c Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 25 Feb 2025 17:01:26 -0700 Subject: [PATCH 4/7] Add support for ubuntu-style dracut image names Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 4 ++++ internal/pkg/image/initramfs.go | 1 + internal/pkg/image/initramfs_test.go | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b7700a..378325b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## v4.6.0, unreleased +### Added + +- Support Ubuntu-style dracut initrd images. + ### Fixed - Fix nightly builds. diff --git a/internal/pkg/image/initramfs.go b/internal/pkg/image/initramfs.go index 85a69010..0a7b7777 100644 --- a/internal/pkg/image/initramfs.go +++ b/internal/pkg/image/initramfs.go @@ -14,6 +14,7 @@ var ( initramfsSearchPaths = []string{ "/boot/initramfs-*", "/boot/initrd-*", + "/boot/initrd.img-*", } versionPattern *regexp.Regexp diff --git a/internal/pkg/image/initramfs_test.go b/internal/pkg/image/initramfs_test.go index ce81c98f..0f4f680d 100644 --- a/internal/pkg/image/initramfs_test.go +++ b/internal/pkg/image/initramfs_test.go @@ -20,7 +20,6 @@ func TestFindInitramfs(t *testing.T) { assert.NoError(t, os.MkdirAll(filepath.Join(RootFsDir("image"), "boot"), 0700)) tests := map[string]struct { - name string initramfs []string ver string path string @@ -45,6 +44,11 @@ func TestFindInitramfs(t *testing.T) { ver: "1.1.1", path: "/boot/initrd-1.1.1.aarch64.img", }, + "ok case 5 (ubuntu)": { + initramfs: []string{"/boot/initrd.img-6.11.0-18-generic"}, + ver: "6.11.0-18", + path: "/boot/initrd.img-6.11.0-18-generic", + }, "prefix match": { initramfs: []string{"/boot/initrd-1.1.1.aarch64.img"}, ver: "1.1", From 8fe5d2dd5f3d7b0b036dc4ce0bb41d299a1fdf11 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 27 Feb 2025 01:00:51 -0700 Subject: [PATCH 5/7] Fix the version of staticcheck used to v0.5.1 v0.6.0, recently released, depends on a newer golang than we might test with. Signed-off-by: Jonathon Anderson --- Tools.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools.mk b/Tools.mk index 9bc49836..924bb2f2 100644 --- a/Tools.mk +++ b/Tools.mk @@ -59,7 +59,7 @@ $(GOLANG_LICENSES): GOBIN="$(PWD)/$(TOOLS_BIN)" go install github.com/google/go-licenses@v1.6.0 $(GOLANG_STATICCHECK): - GOBIN="$(PWD)/$(TOOLS_BIN)" go install honnef.co/go/tools/cmd/staticcheck@latest + GOBIN="$(PWD)/$(TOOLS_BIN)" go install honnef.co/go/tools/cmd/staticcheck@v0.5.1 .PHONY: cleantools cleantools: From c8c9442178e7beb953a0669d6426b648b3a3be0a Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 27 Feb 2025 01:20:35 -0700 Subject: [PATCH 6/7] Change upgraded default kernel args to match distributed `nodes.conf` Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 4 ++++ internal/pkg/upgrade/node.go | 2 +- internal/pkg/upgrade/node_test.go | 6 ------ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 378325b0..3f426030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Support Ubuntu-style dracut initrd images. +### Changed + +- Match default kernel arguments from `wwctl upgrade nodes` with the distributed `nodes.conf`. + ### Fixed - Fix nightly builds. diff --git a/internal/pkg/upgrade/node.go b/internal/pkg/upgrade/node.go index 46cf5d63..3e8fa26f 100644 --- a/internal/pkg/upgrade/node.go +++ b/internal/pkg/upgrade/node.go @@ -123,7 +123,7 @@ func (legacy *NodesYaml) Upgrade(addDefaults bool, replaceOverlays bool, warewul defaultProfile.RuntimeOverlay, genericSplitOverlays...) } if len(defaultProfile.Kernel.Args) < 1 { - defaultProfile.Kernel.Args = []string{"quiet", "crashkernel=no", "vga=791", "net.naming-scheme=v238"} + defaultProfile.Kernel.Args = []string{"quiet", "crashkernel=no"} } if defaultProfile.Init == "" { defaultProfile.Init = "/sbin/init" diff --git a/internal/pkg/upgrade/node_test.go b/internal/pkg/upgrade/node_test.go index fa04fea5..132b109b 100644 --- a/internal/pkg/upgrade/node_test.go +++ b/internal/pkg/upgrade/node_test.go @@ -576,8 +576,6 @@ nodeprofiles: args: - quiet - crashkernel=no - - vga=791 - - net.naming-scheme=v238 init: /sbin/init root: initramfs resources: @@ -656,8 +654,6 @@ nodeprofiles: args: - quiet - crashkernel=no - - vga=791 - - net.naming-scheme=v238 init: /sbin/init root: initramfs resources: @@ -919,8 +915,6 @@ nodeprofiles: args: - quiet - crashkernel=no - - vga=791 - - net.naming-scheme=v238 init: /sbin/init root: initramfs resources: From a97c204f53a8a0059061d0b1bc2820698601ad56 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 26 Feb 2025 17:07:31 +0100 Subject: [PATCH 7/7] Use `wwctl overlay --workers=0` to indicate default value By default, uses `runtime.NumCPU()` The default value is rendered in the generated man pages, which made their content dependent on build system characteristics. Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 4 ++++ internal/app/wwctl/overlay/build/main.go | 10 ++++++++-- internal/app/wwctl/overlay/build/root.go | 4 +--- internal/app/wwctl/overlay/imprt/main.go | 7 ++++++- internal/app/wwctl/overlay/imprt/root.go | 4 +--- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f426030..d3575041 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix nightly builds. +### Changed + +- User `wwctl overlay --workers=0` to indicate `runtime.NumCPU()`. #1782 + ## v4.6.0rc3, 2025-02-23 ### Added diff --git a/internal/app/wwctl/overlay/build/main.go b/internal/app/wwctl/overlay/build/main.go index fd10342c..22e4b9f8 100644 --- a/internal/app/wwctl/overlay/build/main.go +++ b/internal/app/wwctl/overlay/build/main.go @@ -3,6 +3,7 @@ package build import ( "errors" "fmt" + "runtime" "strings" "syscall" @@ -67,10 +68,15 @@ func CobraRunE(cmd *cobra.Command, args []string) error { oldMask := syscall.Umask(000) defer syscall.Umask(oldMask) + workers := Workers + if workers <= 0 { + workers = runtime.NumCPU() + } + if len(OverlayNames) > 0 { - err = overlay.BuildSpecificOverlays(filteredNodes, allNodes, OverlayNames, Workers) + err = overlay.BuildSpecificOverlays(filteredNodes, allNodes, OverlayNames, workers) } else { - err = overlay.BuildAllOverlays(filteredNodes, allNodes, Workers) + err = overlay.BuildAllOverlays(filteredNodes, allNodes, workers) } if err != nil { diff --git a/internal/app/wwctl/overlay/build/root.go b/internal/app/wwctl/overlay/build/root.go index ba1fe163..2c4fac86 100644 --- a/internal/app/wwctl/overlay/build/root.go +++ b/internal/app/wwctl/overlay/build/root.go @@ -1,8 +1,6 @@ package build import ( - "runtime" - "github.com/spf13/cobra" "github.com/warewulf/warewulf/internal/app/wwctl/completions" ) @@ -29,7 +27,7 @@ func init() { } baseCmd.PersistentFlags().StringVarP(&OverlayDir, "output", "o", "", `Do not create an overlay image for distribution but write to the given directory. An overlay must also be ge given to use this option.`) - baseCmd.PersistentFlags().IntVar(&Workers, "workers", runtime.NumCPU(), "The number of parallel workers building overlays") + baseCmd.PersistentFlags().IntVar(&Workers, "workers", 0, "The number of parallel workers building overlays (<=0 indicates 1 worker per CPU)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/overlay/imprt/main.go b/internal/app/wwctl/overlay/imprt/main.go index 890950bc..335ab673 100644 --- a/internal/app/wwctl/overlay/imprt/main.go +++ b/internal/app/wwctl/overlay/imprt/main.go @@ -5,6 +5,7 @@ import ( "os" "path" "path/filepath" + "runtime" "github.com/spf13/cobra" "github.com/warewulf/warewulf/internal/pkg/node" @@ -88,7 +89,11 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { } } - return overlay.BuildSpecificOverlays(updateNodes, nodes, []string{overlayName}, Workers) + workers := Workers + if workers <= 0 { + workers = runtime.NumCPU() + } + return overlay.BuildSpecificOverlays(updateNodes, nodes, []string{overlayName}, workers) } return nil diff --git a/internal/app/wwctl/overlay/imprt/root.go b/internal/app/wwctl/overlay/imprt/root.go index 0c1f6b49..8d54521e 100644 --- a/internal/app/wwctl/overlay/imprt/root.go +++ b/internal/app/wwctl/overlay/imprt/root.go @@ -1,8 +1,6 @@ package imprt import ( - "runtime" - "github.com/spf13/cobra" "github.com/warewulf/warewulf/internal/app/wwctl/completions" ) @@ -34,7 +32,7 @@ var ( func init() { baseCmd.PersistentFlags().BoolVarP(&NoOverlayUpdate, "noupdate", "n", false, "Don't update overlays") baseCmd.PersistentFlags().BoolVarP(&CreateDirs, "parents", "p", false, "Create any necessary parent directories") - baseCmd.PersistentFlags().IntVar(&Workers, "workers", runtime.NumCPU(), "The number of parallel workers building overlays") + baseCmd.PersistentFlags().IntVar(&Workers, "workers", 0, "The number of parallel workers building overlays (<=0 indicates 1 worker per CPU)") } // GetRootCommand returns the root cobra.Command for the application.