diff --git a/cmd/git.go b/cmd/git.go index c26c62b..cdc3429 100644 --- a/cmd/git.go +++ b/cmd/git.go @@ -37,11 +37,11 @@ var gitInstallHooksCmd = &cobra.Command{ ctx.DryRun = true ctx.OnEachMavenProject("installing git hooks", func(repository maven.Repository, project config.Project) error { - hooks, err := project.CloudConfig.GitHookFiles(gitHooksFolderName) + hooks, err := ctx.CloudConfig.GitHookFiles(gitHooksFolderName) if err != nil { return err } - gitHookPath := file.Path("%s/%s", project.CloudConfig.Implementation().Dir(), gitHooksFolderName) + gitHookPath := file.Path("%s/%s", ctx.CloudConfig.Implementation().Dir(), gitHooksFolderName) return shell.InstallGitHooks(gitHookPath, hooks, project.Path) }, ) diff --git a/cmd/merge.go b/cmd/merge.go index 121dc14..183be8c 100644 --- a/cmd/merge.go +++ b/cmd/merge.go @@ -97,7 +97,7 @@ var mergeTemplateCmd = &cobra.Command{ log.Fatalln(err) } - cloudTemplate, err := project.CloudConfig.Template(templateName) + cloudTemplate, err := ctx.CloudConfig.Template(templateName) if err != nil { log.Fatalln(err) } diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 0000000..738749f --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,74 @@ +package cmd + +import ( + "github.com/devdimensionlab/co-pilot/pkg/shell" + "github.com/spf13/cobra" + "strings" +) + +var runCmd = &cobra.Command{ + Use: "run", + Short: "Run scrips in scripts directory", + Long: `Run scrips in scripts directory`, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if err := InitGlobals(cmd); err != nil { + log.Fatalln(err) + } + if err := ctx.FindAndPopulateMavenProjects(); err != nil { + log.Fatalln(err) + } + }, + Run: func(cmd *cobra.Command, args []string) { + scripts, err := ctx.CloudConfig.Scripts() + if err != nil { + log.Fatalln(err) + } + + list, err := cmd.Flags().GetBool("list") + if err != nil { + log.Fatalln(err) + } + + if list || len(args) == 0 { + log.Infoln("Available scripts:") + for _, script := range scripts { + log.Infoln("- " + strings.TrimSuffix(script.Name, ".sh")) + } + return + } + + requestedScript := args[0] + script, err := ctx.CloudConfig.Script(requestedScript) + if err != nil { + log.Fatalln(err) + } + + envMap := make(map[string]interface{}) + localConfigMap, err := ctx.LocalConfig.ConfigAsMap() + if err != nil { + log.Fatalln(err) + } + envMap["local_config"] = localConfigMap + + if len(ctx.Projects) > 0 { + projectConfig, err := ctx.Projects[0].ConfigAsMap() + if err != nil { + log.Fatalln(err) + } + envMap["project_config"] = projectConfig + } + + output, err := shell.RunWithEnvironment(envMap, script.Path, args[1:]...) + if err != nil { + log.Fatalln(err) + } + println(string(output)) + }, +} + +func init() { + RootCmd.AddCommand(runCmd) + + runCmd.PersistentFlags().StringVar(&ctx.TargetDirectory, "target", ".", "Optional target directory") + runCmd.Flags().Bool("list", false, "list available scripts") +} diff --git a/cmd/status.go b/cmd/status.go index b5fb48b..526383e 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -41,7 +41,7 @@ var statusCmd = &cobra.Command{ maven.UpgradePlugins(), maven.ChangeVersionToPropertyTags(), maven.CleanManualVersions(), - maven.StatusDeprecated(), + maven.StatusDeprecated(ctx.CloudConfig), ) } }, diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 82755c7..0d0a4ef 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -94,7 +94,7 @@ var upgradeDeprecatedCmd = &cobra.Command{ Long: `Remove and replace deprecated dependencies in a project`, Run: func(cmd *cobra.Command, args []string) { ctx.OnEachMavenProject("removes and replaces deprecated dependencies", func(repository maven.Repository, project config.Project) error { - return upgradeDeprecated(project) + return upgradeDeprecated(ctx.CloudConfig, project) }) }, } @@ -160,8 +160,8 @@ func init() { upgradeCmd.PersistentFlags().BoolVar(&ctx.StealthMode, "stealth", false, "use alternative pom.xml writer") } -func upgradeDeprecated(project config.Project) error { - templates, err := maven.RemoveDeprecated(project.CloudConfig, project.Type.Model()) +func upgradeDeprecated(cloudConfig config.CloudConfig, project config.Project) error { + templates, err := maven.RemoveDeprecated(cloudConfig, project.Type.Model()) if err != nil { return err } diff --git a/pkg/config/cloud.go b/pkg/config/cloud.go index 5069fbb..4f9efaf 100644 --- a/pkg/config/cloud.go +++ b/pkg/config/cloud.go @@ -30,6 +30,8 @@ type CloudConfig interface { ValidTemplatesFrom(list []string) (templates []CloudTemplate, err error) Templates() (templates []CloudTemplate, err error) Template(name string) (CloudTemplate, error) + Scripts() (scripts []CloudScript, err error) + Script(name string) (CloudScript, error) Examples() (templates []string, err error) GlobalCloudConfig() (globalCloudConfig GlobalCloudConfig, err error) } @@ -247,6 +249,43 @@ func (gitCfg GitCloudConfig) Templates() (templates []CloudTemplate, err error) return } +func (gitCfg GitCloudConfig) Scripts() (scripts []CloudScript, err error) { + root := file.Path("%s/scripts", gitCfg.Implementation().Dir()) + + err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !strings.Contains(info.Name(), ".sh") { + return nil + } + scripts = append(scripts, CloudScript{ + Name: info.Name(), + Path: path, + }) + return nil + }) + + return +} + +func (gitCfg GitCloudConfig) Script(name string) (script CloudScript, err error) { + scripts, err := gitCfg.Scripts() + if err != nil { + return script, err + } + + for _, s := range scripts { + if s.Name == name || s.Name == name+".sh" { + script = s + return + } + } + + return script, errors.New(fmt.Sprintf("could not find any scripts with name: %s", name)) +} + func (gitCfg GitCloudConfig) Examples() (templates []string, err error) { examplesDir := file.Path("%s/examples", gitCfg.Implementation().Dir()) items, err := ioutil.ReadDir(examplesDir) diff --git a/pkg/config/local.go b/pkg/config/local.go index 8a6bf63..ec7920d 100644 --- a/pkg/config/local.go +++ b/pkg/config/local.go @@ -1,6 +1,7 @@ package config import ( + "encoding/json" "github.com/devdimensionlab/co-pilot/pkg/file" "gopkg.in/yaml.v2" "os" @@ -133,6 +134,22 @@ func (localCfgDir LocalConfigDir) Config() (LocalConfiguration, error) { return config, nil } +func (localCfgDir LocalConfigDir) ConfigAsMap() (map[string]any, error) { + config, err := localCfgDir.Config() + if err != nil { + return nil, err + } + + serializedBytes, err := json.Marshal(config) + if err != nil { + return nil, err + } + + var deserialized map[string]any + err = json.Unmarshal(serializedBytes, &deserialized) + return deserialized, err +} + func (localCfgDir LocalConfigDir) Print() error { c, err := localCfgDir.Config() if err != nil { diff --git a/pkg/config/project.go b/pkg/config/project.go index 9717d94..3fa9f59 100644 --- a/pkg/config/project.go +++ b/pkg/config/project.go @@ -18,12 +18,11 @@ import ( const projectConfigFileName = "co-pilot.json" type Project struct { - Path string - GitInfo GitInfo - ConfigFile string - Config ProjectConfiguration - Type ProjectType - CloudConfig CloudConfig + Path string + GitInfo GitInfo + ConfigFile string + Config ProjectConfiguration + Type ProjectType } type ValidProjectType string @@ -290,6 +289,17 @@ func (project *Project) SortAndWritePom() error { return project.Type.Model().WriteToFile(outputFile, indentation) } +func (project *Project) ConfigAsMap() (map[string]any, error) { + serializedBytes, err := json.Marshal(project) + if err != nil { + return nil, err + } + + var deserialized map[string]any + err = json.Unmarshal(serializedBytes, &deserialized) + return deserialized, err +} + func (projectSettings *ProjectSettings) DependencyIsIgnored(dep pom.Dependency) bool { if projectSettings.DisableUpgradesFor == nil { return false diff --git a/pkg/config/project_init.go b/pkg/config/project_init.go index 5215892..929538a 100644 --- a/pkg/config/project_init.go +++ b/pkg/config/project_init.go @@ -75,25 +75,6 @@ func InitProjectFromDirectory(targetDir string) (project Project, err error) { return } - profilePath, err := GetActiveProfilePath() - if err != nil && strings.Contains(err.Error(), "no such file or directory") { - if err := InstallOrMigrateToProfiles(); err != nil { - return project, err - } - profilePath, err = GetActiveProfilePath() - if err != nil { - return project, err - } - } - - if project.Config.Profile != "" { - profilePath, err = GetProfilesPathFor(project.Config.Profile) - if err != nil { - return - } - } - project.CloudConfig = OpenGitCloudConfig(profilePath) - pomFile := file.Path("%s/pom.xml", targetDir) if file.Exists(pomFile) { pomModel, err := pom.GetModelFrom(pomFile) diff --git a/pkg/config/types.go b/pkg/config/types.go index 99ccc96..13e2c83 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -94,6 +94,11 @@ type CloudTemplate struct { Project Project } +type CloudScript struct { + Name string + Path string +} + type CloudProjectDefaults struct { Type string `json:"type"` Settings ProjectSettings `json:"settings"` diff --git a/pkg/context/context.go b/pkg/context/context.go index ce4b86f..dc4fabf 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -15,9 +15,9 @@ type Context struct { ForceCloudSync bool OpenInBrowser bool StealthMode bool - Projects []config.Project Err error ProfilesPath string + Projects []config.Project LocalConfig config.LocalConfigDir CloudConfig config.CloudConfig } @@ -60,8 +60,8 @@ func (ctx *Context) OnEachMavenProject(description string, do ...func(repository mavenRepository := ctx.GetMavenRepository() for _, p := range ctx.Projects { - if p.CloudConfig != nil { - projectDefaults, err := p.CloudConfig.ProjectDefaults() + if ctx.CloudConfig != nil { + projectDefaults, err := ctx.CloudConfig.ProjectDefaults() if err != nil { log.Warnf("could not find a project-defaults.json file in cloud-config") log.Debugf("%v", err) diff --git a/pkg/maven/deprecated.go b/pkg/maven/deprecated.go index a3487bd..9635118 100644 --- a/pkg/maven/deprecated.go +++ b/pkg/maven/deprecated.go @@ -40,9 +40,8 @@ func RemoveDeprecated(cloudConfig config.CloudConfig, model *pom.Model) (templat return } -func StatusDeprecated() func(repository Repository, project config.Project) error { +func StatusDeprecated(cloudConfig config.CloudConfig) func(repository Repository, project config.Project) error { return func(repository Repository, project config.Project) error { - cloudConfig := project.CloudConfig model := project.Type.Model() if model == nil || model.Dependencies == nil { return nil diff --git a/pkg/shell/command.go b/pkg/shell/command.go index 13e5462..443e00e 100644 --- a/pkg/shell/command.go +++ b/pkg/shell/command.go @@ -30,6 +30,30 @@ func Run(name string, args ...string) Output { return run(exec.Command(name, args...)) } +func RunWithEnvironment(environment map[string]interface{}, name string, args ...string) ([]byte, error) { + cmd := exec.Command(name, args...) + cmd.Env = os.Environ() + flatMap := make(map[string]interface{}) + flatten("CO_PILOT", environment, flatMap) + for k, v := range flatMap { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", strings.ToUpper(k), v)) + } + return cmd.Output() +} + +func flatten(rootKey string, a map[string]interface{}, output map[string]interface{}) { + for k, v := range a { + switch v.(type) { + case map[string]interface{}: + flatten(fmt.Sprintf("%s_%s", rootKey, k), v.(map[string]interface{}), output) + default: + key := strings.ToUpper(fmt.Sprintf("%s_%s", rootKey, k)) + output[key] = v + } + } + return +} + func run(cmd *exec.Cmd) (output Output) { log.Debugf("running: %s", cmd.String()) cmd.Stdout = &output.StdOut