Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"perfspect/cmd/report"
"perfspect/cmd/telemetry"
"perfspect/internal/app"
"perfspect/internal/script"
"perfspect/internal/util"

"github.com/pkg/errors"
Expand Down Expand Up @@ -114,6 +115,7 @@ Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
rootCmd.AddCommand(config.Cmd)
rootCmd.AddGroup([]*cobra.Group{{ID: "other", Title: "Other Commands:"}}...)
rootCmd.AddCommand(updateCmd)
rootCmd.AddCommand(extractCmd)
// Global (persistent) flags
rootCmd.PersistentFlags().BoolVar(&flagDebug, app.FlagDebugName, false, "enable debug logging and retain temporary directories")
rootCmd.PersistentFlags().BoolVar(&flagSyslog, app.FlagSyslogName, false, "write logs to syslog instead of a file")
Expand Down Expand Up @@ -489,6 +491,29 @@ func getLatestManifest() (manifest, error) {
return latestManifest, nil
}

// define the extract command
const (
extractCommandName = "extract"
)

var extractCmd = &cobra.Command{
GroupID: "other",
Use: extractCommandName,
Short: "Extract the embedded resources (for developers)",
RunE: func(cmd *cobra.Command, args []string) error {
appContext := cmd.Parent().Context().Value(app.Context{}).(app.Context)
// extract the internal/script module's embedded resources
err := util.ExtractAllResources(script.Resources, appContext.OutputDir)
if err != nil {
slog.Error("Failed to extract script resources", slog.String("error", err.Error()))
fmt.Printf("Error: failed to extract script resources: %v\n", err)
return err
}
fmt.Printf("Extracted script resources to %s\n", appContext.OutputDir)
return nil
},
}

// SyslogHandler is a slog.Handler that logs to syslog.
type SyslogHandler struct {
writer *syslog.Writer
Expand Down
25 changes: 25 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ func GeoMean(vals []float64) (val float64) {
return
}

// ExtractAllResources recurcively extracts all resources as files into the specified directory.
func ExtractAllResources(resources embed.FS, dir string) error {
// walk the embedded filesystem starting at "resources"
return fs.WalkDir(resources, "resources", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel("resources", path)
if err != nil {
return err
}
relPath = filepath.Join(dir, relPath)
if d.IsDir() {
if relPath == "." {
return nil
}
// create directory
return os.MkdirAll(relPath, 0700)
}
// extract file
_, err = ExtractResource(resources, path, filepath.Dir(relPath))
return err
})
}

// ExtractResource extracts a resource from the given embed.FS and saves it to the specified temporary directory.
// It returns the path to the saved resource file and any error encountered during the process.
func ExtractResource(resources embed.FS, resourcePath string, tempDir string) (string, error) {
Expand Down