diff --git a/cmd/checkout.go b/cmd/checkout.go index b28509d..ed78a41 100644 --- a/cmd/checkout.go +++ b/cmd/checkout.go @@ -29,6 +29,11 @@ Examples: $ graylog-project checkout manifests/master.json $ graylog-project co manifests/master.json /path/to/other/manifest.json + + # Override graylog-plugin-collector module in manifest to checkout revision + # "abc123" of the a-contributor/graylog-plugin-collector repository + # instead of Graylog2/graylog-plugin-collector + $ graylog-project co --module-override Graylog2/graylog-plugin-collector=a-contributor/graylog-plugin-collector@abc123 `, Run: checkoutCommand, } @@ -40,11 +45,13 @@ func init() { checkoutCmd.Flags().BoolP("shallow-clone", "s", false, "Create a shallow git clone instead of a regular one") checkoutCmd.Flags().BoolP("force", "f", false, "Force checkout event though repository is unexpected") checkoutCmd.Flags().StringP("auth-token", "T", "", "Auth token to access protected URLs") + checkoutCmd.Flags().StringSliceP("module-override", "O", []string{}, "Override manifest modules, see help for details") viper.BindPFlag("checkout.update-repos", checkoutCmd.Flags().Lookup("update-repos")) viper.BindPFlag("checkout.shallow-clone", checkoutCmd.Flags().Lookup("shallow-clone")) viper.BindPFlag("checkout.force", checkoutCmd.Flags().Lookup("force")) viper.BindPFlag("checkout.auth-token", checkoutCmd.Flags().Lookup("auth-token")) + viper.BindPFlag("checkout.module-override", checkoutCmd.Flags().Lookup("module-override")) viper.BindEnv("checkout.auth-token", "GPC_AUTH_TOKEN") } @@ -73,7 +80,7 @@ func prepareCheckoutCommand(cmd *cobra.Command, args []string) (c.Config, *repo. logger.Debug("Using manifests: %v", config.Checkout.ManifestFiles) - project := p.New(config, config.Checkout.ManifestFiles) + project := p.New(config, config.Checkout.ManifestFiles, p.WithModuleOverride()) return config, repoManager, project } diff --git a/config/config.go b/config/config.go index 0c60039..4271de6 100644 --- a/config/config.go +++ b/config/config.go @@ -11,10 +11,11 @@ const DefaultRepositoryRoot = "../graylog-project-repos" const CIRepositoryRoot = ".repos" type Checkout struct { - UpdateRepos bool `mapstructure:"update-repos"` - ShallowClone bool `mapstructure:"shallow-clone"` - ManifestFiles []string `mapstructure:"manifest-files"` - Force bool `mapstructure:"force"` + UpdateRepos bool `mapstructure:"update-repos"` + ShallowClone bool `mapstructure:"shallow-clone"` + ManifestFiles []string `mapstructure:"manifest-files"` + Force bool `mapstructure:"force"` + ModuleOverride []string `mapstructure:"module-override"` } type ApplyManifest struct { diff --git a/project/project.go b/project/project.go index 74b05b6..d547439 100644 --- a/project/project.go +++ b/project/project.go @@ -103,7 +103,25 @@ func getMavenCoordinates(path string) pomparse.MavenCoordinates { return pomparse.GetMavenCoordinates(filepath.Join(path, "pom.xml")) } -func New(config config.Config, manifestFiles []string) Project { +type projectOptions struct { + moduleOverride bool +} + +type projectOption func(*projectOptions) + +func WithModuleOverride() projectOption { + return func(o *projectOptions) { + o.moduleOverride = true + } +} + +func New(config config.Config, manifestFiles []string, options ...projectOption) Project { + // Create a new project options object and process all given options + opt := projectOptions{} + for _, o := range options { + o(&opt) + } + readManifest := manifest.ReadManifest(manifestFiles) var server Module @@ -194,6 +212,10 @@ func New(config config.Config, manifestFiles []string) Project { logger.Fatal("No server module in manifests: %v", manifestFiles) } + if len(config.Checkout.ModuleOverride) > 0 && opt.moduleOverride { + projectModules = applyModuleOverride(config, projectModules) + } + project := Project{ config: config, Server: server, @@ -203,6 +225,52 @@ func New(config config.Config, manifestFiles []string) Project { return project } +func applyModuleOverride(c config.Config, modules []Module) []Module { + newModules := make([]Module, 0) + + // We check if there is an override for any module in our manifests + for _, module := range modules { + for _, override := range c.Checkout.ModuleOverride { + // The override option is "=@ + parts := strings.SplitN(override, "=", 2) + if len(parts) != 2 { + logger.Error("invalid override <%s> - skipping", override) + continue + } + matchString, replacement := parts[0], parts[1] + + // Now split the replacement + replacementParts := strings.SplitN(replacement, "@", 2) + if len(parts) != 2 { + logger.Error("invalid override <%s> - skipping", override) + continue + } + replacementRepo, replacementRev := replacementParts[0], replacementParts[1] + + // The repo-match-substring of the override is used to select if the current module has + // an override + if strings.Contains(module.Repository, matchString) { + // Build a new repo URL depending on the original type. (SSH, HTTPS, ...) + newRepo, err := utils.ReplaceGitHubURL(module.Repository, replacementRepo) + if err != nil { + logger.Error("couldn't replace repository for module <%S>: %v", module.Name, err) + continue + } + + logger.Info("Overriding <%s@%s> with <%s@%s> for module <%s>", + module.Repository, module.Revision, newRepo, replacementRev, module.Name) + + module.Repository = newRepo + module.Revision = replacementRev + } + } + + newModules = append(newModules, module) + } + + return newModules +} + func getModulePath(repositoryPath string, name string, module manifest.ManifestModule) string { if module.Path == "" { return filepath.Join(repositoryPath, name) diff --git a/utils/github.go b/utils/github.go index 0e7c9cc..8d53a80 100644 --- a/utils/github.go +++ b/utils/github.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "github.com/pkg/errors" "path/filepath" "strings" @@ -27,6 +28,21 @@ func ParseGitHubURL(url string) (GitHubURL, error) { return gitHubURL, nil } +func ReplaceGitHubURL(url string, repoName string) (string, error) { + name := strings.TrimSuffix(repoName, ".git") + + switch { + case strings.HasPrefix(url, "github://"): + return fmt.Sprintf("github://%s.git", name), nil + case strings.HasPrefix(url, "git@github"): + return fmt.Sprintf("git@github.com:%s.git", name), nil + case strings.HasPrefix(url, "https://github"): + return fmt.Sprintf("https://github.com/%s.git", name), nil + default: + return "", errors.Errorf("unknown GitHub URL: %s", url) + } +} + type GitHubURL struct { Repository string } diff --git a/utils/github_test.go b/utils/github_test.go index 93cfc47..25178c3 100644 --- a/utils/github_test.go +++ b/utils/github_test.go @@ -65,3 +65,38 @@ func TestParseGitHubURL(t *testing.T) { t.Error("expected unknown URL to fail") } } + +func TestReplaceGitHubURL(t *testing.T) { + url, _ := ReplaceGitHubURL("github://Graylog2/graylog2-server.git", "foo/graylog2-server") + expected := "github://foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } + url, _ = ReplaceGitHubURL("github://Graylog2/graylog2-server.git", "foo/graylog2-server.git") + expected = "github://foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } + + url, _ = ReplaceGitHubURL("https://github.com/Graylog2/graylog2-server.git", "foo/graylog2-server") + expected = "https://github.com/foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } + url, _ = ReplaceGitHubURL("https://github.com/Graylog2/graylog2-server.git", "foo/graylog2-server.git") + expected = "https://github.com/foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } + + url, _ = ReplaceGitHubURL("https://github.com/Graylog2/graylog2-server.git", "foo/graylog2-server") + expected = "https://github.com/foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } + url, _ = ReplaceGitHubURL("https://github.com/Graylog2/graylog2-server.git", "foo/graylog2-server.git") + expected = "https://github.com/foo/graylog2-server.git" + if url != expected { + t.Errorf("expected <%s> but got <%s>", expected, url) + } +}