diff --git a/main.go b/main.go index 9464a16..1ee6891 100644 --- a/main.go +++ b/main.go @@ -5,31 +5,37 @@ package main import ( + "fmt" "io" "net/http" + "github.com/meltwater/drone-convert-pathschanged/plugin" + "github.com/drone/drone-go/plugin/converter" _ "github.com/joho/godotenv/autoload" "github.com/kelseyhightower/envconfig" - "github.com/meltwater/drone-convert-pathschanged/plugin" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" ) // spec provides the plugin settings. -type spec struct { - Bind string `envconfig:"DRONE_BIND"` - Debug bool `envconfig:"DRONE_DEBUG"` - Text bool `envconfig:"DRONE_LOGS_TEXT"` - Secret string `envconfig:"DRONE_SECRET"` - - Provider string `envconfig:"PROVIDER"` - Token string `envconfig:"TOKEN"` - BitBucketAddress string `envconfig:"BB_ADDRESS"` - BitBucketUser string `envconfig:"BITBUCKET_USER"` - BitBucketPassword string `envconfig:"BITBUCKET_PASSWORD"` - GithubServer string `envconfig:"GITHUB_SERVER"` -} +type ( + spec struct { + Bind string `envconfig:"DRONE_BIND"` + Debug bool `envconfig:"DRONE_DEBUG"` + Text bool `envconfig:"DRONE_LOGS_TEXT"` + Secret string `envconfig:"DRONE_SECRET"` + + Provider string `envconfig:"PROVIDER"` + Token string `envconfig:"TOKEN"` + // BB_ADDRESS is deprecated in favor of STASH_SERVER, it will be removed in a future version + BitBucketAddress string `envconfig:"BB_ADDRESS"` + BitBucketUser string `envconfig:"BITBUCKET_USER"` + BitBucketPassword string `envconfig:"BITBUCKET_PASSWORD"` + GithubServer string `envconfig:"GITHUB_SERVER"` + StashServer string `envconfig:"STASH_SERVER"` + } +) func contains(s []string, str string) bool { for _, v := range s { @@ -40,11 +46,54 @@ func contains(s []string, str string) bool { return false } +func validate(spec *spec) error { + if spec.Secret == "" { + return fmt.Errorf("missing secret key") + } + if spec.Provider == "" { + return fmt.Errorf("missing provider") + } else { + providers := []string{ + "bitbucket", + // bitbucket-server support is deprecated in favor of stash, it will be removed in a future version + "bitbucket-server", + "github", + "stash", + } + if !contains(providers, spec.Provider) { + return fmt.Errorf("unsupported provider") + } + } + if spec.Token == "" && (spec.Provider == "github" || spec.Provider == "bitbucket-server" || spec.Provider == "stash") { + return fmt.Errorf("missing token") + } + if spec.BitBucketUser == "" && spec.Provider == "bitbucket" { + return fmt.Errorf("missing bitbucket user") + } + if spec.BitBucketPassword == "" && spec.Provider == "bitbucket" { + return fmt.Errorf("missing bitbucket password") + } + if spec.BitBucketAddress == "" && spec.Provider == "bitbucket-server" { + return fmt.Errorf("missing bitbucket server address") + } else if spec.BitBucketAddress != "" && spec.Provider == "bitbucket-server" { + // backwards compatible support for bitbucket-server, this will be removed in a future version + spec.StashServer = spec.BitBucketAddress + spec.Provider = "stash" + + logrus.Warningln("bitbucket-server support is deprecated, please use stash") + } + if spec.StashServer == "" && spec.Provider == "stash" { + return fmt.Errorf("missing stash server") + } + + return nil +} + func main() { spec := new(spec) err := envconfig.Process("", spec) if err != nil { - logrus.Fatal(err) + logrus.Fatalln(err) } if spec.Debug { @@ -55,41 +104,26 @@ func main() { } else { logrus.SetFormatter(&logrus.JSONFormatter{}) } - if spec.Secret == "" { - logrus.Fatalln("missing secret key") - } - if spec.Provider == "" { - logrus.Fatalln("missing provider") - } else { - providers := []string{"bitbucket", "bitbucket-server", "github"} - if !contains(providers, spec.Provider) { - logrus.Fatalln("invalid provider:", spec.Provider) - } - } - if spec.Token == "" && (spec.Provider == "github" || spec.Provider == "bitbucket-server") { - logrus.Fatalln("missing token") - } - if spec.BitBucketUser == "" && spec.Provider == "bitbucket" { - logrus.Fatalln("missing bitbucket user") - } - if spec.BitBucketPassword == "" && spec.Provider == "bitbucket" { - logrus.Fatalln("missing bitbucket password") - } - if spec.BitBucketAddress == "" && spec.Provider == "bitbucket-server" { - logrus.Fatalln("missing bitbucket server address") + + err = validate(spec) + if err != nil { + logrus.Fatalln(err) } + if spec.Bind == "" { spec.Bind = ":3000" } + params := &plugin.Params{ + BitBucketUser: spec.BitBucketUser, + BitBucketPassword: spec.BitBucketPassword, + GithubServer: spec.GithubServer, + Token: spec.Token, + StashServer: spec.StashServer, + } + handler := converter.Handler( - plugin.New( - spec.Token, - spec.Provider, - spec.GithubServer, - spec.BitBucketUser, - spec.BitBucketPassword, - ), + plugin.New(spec.Provider, params), spec.Secret, logrus.StandardLogger(), ) diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..6a3a613 --- /dev/null +++ b/main_test.go @@ -0,0 +1,161 @@ +package main + +import ( + "testing" +) + +func TestValidateSecretMissing(t *testing.T) { + s := &spec{ + Secret: "", + } + + got := validate(s) + + want := "missing secret key" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +func TestValidateProviderMissing(t *testing.T) { + s := &spec{ + Provider: "", + Secret: "abcdefg", + } + + got := validate(s) + + want := "missing provider" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +func TestValidateProviderUnsupported(t *testing.T) { + s := &spec{ + Provider: "unsupported", + Secret: "abcdefg", + } + + got := validate(s) + + want := "unsupported provider" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +func TestValidateTokenMissing(t *testing.T) { + // bitbucket-server/stash and github use tokens for authentication + providers := []string{ + "bitbucket-server", + "github", + "stash", + } + for _, provider := range providers { + s := &spec{ + Provider: provider, + Secret: "abcdefg", + Token: "", + } + + got := validate(s) + + want := "missing token" + + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } + } +} + +func TestValidateBitbucketUserMissing(t *testing.T) { + s := &spec{ + BitBucketUser: "", + Provider: "bitbucket", + Secret: "abcdefg", + } + + got := validate(s) + + want := "missing bitbucket user" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +func TestValidateBitbucketPasswordMissing(t *testing.T) { + s := &spec{ + BitBucketUser: "centauri", + BitBucketPassword: "", + Provider: "bitbucket", + Secret: "abcdefg", + } + + got := validate(s) + + want := "missing bitbucket password" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +func TestValidateBitbucketServerAddressMissing(t *testing.T) { + s := &spec{ + BitBucketAddress: "", + Provider: "bitbucket-server", + Secret: "abcdefg", + Token: "abcdefg", + } + + got := validate(s) + + want := "missing bitbucket server address" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} + +// this tests backwards compatibility with bitbucket-server for stash +func TestValidateBitbucketServerStashCompatibility(t *testing.T) { + s := &spec{ + BitBucketAddress: "example.com", + Provider: "bitbucket-server", + Secret: "abcdefg", + Token: "abcdefg", + } + + err := validate(s) + if err != nil { + t.Error(err) + } + + // validate should replace 'Provider' with 'stash' and set 'StashServer' + want := &spec{ + BitBucketAddress: "example.com", + Provider: "stash", + StashServer: "example.com", + Secret: "abcdefg", + Token: "abcdefg", + } + + if *s != *want { + t.Errorf("wanted %+v, got %+v", *want, *s) + } +} + +func TestValidateStashServerMissing(t *testing.T) { + s := &spec{ + Provider: "stash", + Secret: "abcdefg", + StashServer: "", + Token: "abcdefg", + } + + got := validate(s) + + want := "missing stash server" + if got.Error() != want { + t.Errorf("wanted %s, got %s", want, got) + } +} diff --git a/plugin/parse.go b/plugin/parse.go index 56f0a14..942eddc 100644 --- a/plugin/parse.go +++ b/plugin/parse.go @@ -114,7 +114,7 @@ func parsePipelines(data string, build drone.Build, repo drone.Repo, changedFile if resource.Trigger.Attrs == nil { resource.Trigger.Attrs = make(map[string]interface{}) } - resource.Trigger.Attrs["event"] = map[string][]string{"exclude": []string{"*"}} + resource.Trigger.Attrs["event"] = map[string][]string{"exclude": {"*"}} } } @@ -146,7 +146,7 @@ func parsePipelines(data string, build drone.Build, repo drone.Repo, changedFile if step.When.Attrs == nil { step.When.Attrs = make(map[string]interface{}) } - step.When.Attrs["event"] = map[string][]string{"exclude": []string{"*"}} + step.When.Attrs["event"] = map[string][]string{"exclude": {"*"}} } } } diff --git a/plugin/plugin.go b/plugin/plugin.go index 0e69a3a..ce4f86d 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -21,13 +21,17 @@ import ( ) type ( + Params struct { + BitBucketUser string + BitBucketPassword string + GithubServer string + StashServer string + Token string + } + plugin struct { - token string - provider string - bitbucketAddress string - bitbucketUser string - bitbucketPassword string - githubAddress string + provider string + params Params } resource struct { @@ -108,13 +112,10 @@ func marshal(in []*resource) ([]byte, error) { } // New returns a new conversion plugin. -func New(token string, provider string, githubAddress string, bitbucketUser string, bitbucketPassword string) converter.Plugin { +func New(provider string, p *Params) converter.Plugin { return &plugin{ - token: token, - provider: provider, - githubAddress: githubAddress, - bitbucketUser: bitbucketUser, - bitbucketPassword: bitbucketPassword, + provider: provider, + params: *p, } } @@ -155,22 +156,22 @@ func (p *plugin) Convert(ctx context.Context, req *converter.Request) (*drone.Co switch p.provider { case "github": - changedFiles, err = providers.GetGithubFilesChanged(req.Repo, req.Build, p.token, p.githubAddress) + changedFiles, err = providers.GetGithubFilesChanged(req.Repo, req.Build, p.params.Token, p.params.GithubServer) if err != nil { return nil, err } case "bitbucket": - changedFiles, err = providers.GetBitbucketFilesChanged(req.Repo, req.Build, p.bitbucketUser, p.bitbucketPassword, scm.ListOptions{}) + changedFiles, err = providers.GetBitbucketFilesChanged(req.Repo, req.Build, p.params.BitBucketUser, p.params.BitBucketPassword, scm.ListOptions{}) if err != nil { return nil, err } - case "bitbucket-server": - changedFiles, err = providers.GetBBFilesChanged(req.Repo, req.Build, p.token) + case "stash": + changedFiles, err = providers.GetStashFilesChanged(req.Repo, req.Build, p.params.StashServer, p.params.Token, scm.ListOptions{}) if err != nil { return nil, err } default: - requestLogger.Errorln("unsupported provider: ", p.provider) + requestLogger.Errorln("unsupported provider:", p.provider) return nil, errors.New("unsupported provider") } diff --git a/plugin/plugin_test.go b/plugin/plugin_test.go index 7781a0c..ba5c18a 100644 --- a/plugin/plugin_test.go +++ b/plugin/plugin_test.go @@ -23,7 +23,7 @@ var noContext = context.Background() func TestNewEmptyPipeline(t *testing.T) { - providers := []string{"github", "bitbucket", "bitbucket-server"} + providers := []string{"github", "bitbucket", "bitbucket-server", "stash"} req := &converter.Request{ Build: drone.Build{}, @@ -33,8 +33,12 @@ func TestNewEmptyPipeline(t *testing.T) { }, } + params := &Params{ + Token: "invalidtoken", + } + for _, provider := range providers { - plugin := New("invalidtoken", provider, "", "", "") + plugin := New(provider, params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -73,7 +77,11 @@ this_is_invalid_yaml }, } - plugin := New("invalidtoken", "", "", "", "") + params := &Params{ + Token: "invalidtoken", + } + + plugin := New("invalidtoken", params) _, err := plugin.Convert(noContext, req) if err == nil { @@ -83,6 +91,7 @@ this_is_invalid_yaml } func TestNewUnsupportedProvider(t *testing.T) { + data := ` kind: pipeline type: docker @@ -99,6 +108,10 @@ steps: - .drone.yml ` + params := &Params{ + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "", @@ -115,10 +128,15 @@ steps: }, } - plugin := New("invalidtoken", "unsupported", "", "", "") + plugin := New("unsupported", params) _, err := plugin.Convert(noContext, req) - if err == nil { + + // just looking for an error here isn't enough, since calling 'New' will always return + // an error during this test, because it can't authenticate with the provider + // + // therefore, look for the specific 'unsupported provided' error + if err.Error() != "unsupported provider" { t.Error("unsupported provider did not return error") return } @@ -148,6 +166,11 @@ steps: exclude: - .drone.yml ` + + params := &Params{ + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "", @@ -164,7 +187,7 @@ steps: }, } - plugin := New("invalidtoken", "github", "", "", "") + plugin := New("github", params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -221,6 +244,11 @@ steps: include: - .drone.yml ` + + params := &Params{ + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "", @@ -237,7 +265,7 @@ steps: }, } - plugin := New("invalidtoken", "github", "", "", "") + plugin := New("github", params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -291,6 +319,11 @@ steps: exclude: - .drone.yml ` + + params := &Params{ + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "496eb80334e84085426ce681407d770cc9247acd", @@ -307,7 +340,7 @@ steps: }, } - plugin := New("invalidtoken", "github", "", "", "") + plugin := New("github", params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -364,6 +397,10 @@ steps: include: - .drone.yml ` + params := &Params{ + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "496eb80334e84085426ce681407d770cc9247acd", @@ -380,7 +417,7 @@ steps: }, } - plugin := New("invalidtoken", "github", "", "", "") + plugin := New("github", params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -434,6 +471,13 @@ steps: exclude: - CONTRIBUTING.md ` + + params := &Params{ + BitBucketUser: "centauri", + BitBucketPassword: "kodan", + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "", @@ -450,7 +494,7 @@ steps: }, } - plugin := New("invalidtoken", "bitbucket", "", "", "") + plugin := New("bitbucket", params) config, err := plugin.Convert(noContext, req) if err != nil { @@ -506,6 +550,12 @@ steps: exclude: - CONTRIBUTING.md ` + params := &Params{ + BitBucketUser: "centauri", + BitBucketPassword: "kodan", + Token: "invalidtoken", + } + req := &converter.Request{ Build: drone.Build{ Before: "dec26e0fe887167743c2b7e36531dedfeb6cd478", @@ -522,7 +572,7 @@ steps: }, } - plugin := New("invalidtoken", "bitbucket", "", "", "") + plugin := New("bitbucket", params) config, err := plugin.Convert(noContext, req) if err != nil { diff --git a/providers/bitbucket_server.go b/providers/bitbucket_server.go deleted file mode 100644 index 2fb5fd7..0000000 --- a/providers/bitbucket_server.go +++ /dev/null @@ -1,50 +0,0 @@ -package providers - -import ( - "context" - "encoding/json" - "github.com/drone/drone-go/drone" - bitbucketv1 "github.com/gfleury/go-bitbucket-v1" - "os" -) - -type bitbucketDiffs struct { - Diffs []struct { - Destination struct { - Name string `json:"toString"` - } `json:"destination"` - } `json:"diffs"` -} - -func GetBBFilesChanged(repo drone.Repo, build drone.Build, token string) ([]string, error) { - var files []string - var ctx context.Context - bitbucketAddress := os.Getenv("BB_ADDRESS") - params := map[string]interface{}{ - "since": build.Before, - } - ctx = context.WithValue(context.Background(), bitbucketv1.ContextAccessToken, token) - - configuration := bitbucketv1.NewConfiguration(bitbucketAddress) - client := bitbucketv1.NewAPIClient(ctx, configuration) - - ff, err := client.DefaultApi.StreamDiff(repo.Namespace, repo.Name, build.After, params) - if err != nil { - return nil, err - } - - jsonString, err := json.Marshal(ff.Values) - if err != nil { - return nil, err - } - res := bitbucketDiffs{} - err = json.Unmarshal([]byte(jsonString), &res) - if err != nil { - return nil, err - } - - for _, diff := range res.Diffs { - files = append(files, diff.Destination.Name) - } - return files, nil -} diff --git a/providers/stash.go b/providers/stash.go new file mode 100644 index 0000000..85a298e --- /dev/null +++ b/providers/stash.go @@ -0,0 +1,47 @@ +package providers + +import ( + "context" + "net/http" + + "github.com/drone/drone-go/drone" + "github.com/drone/go-scm/scm" + "github.com/drone/go-scm/scm/driver/stash" + "github.com/drone/go-scm/scm/transport" +) + +func GetStashFilesChanged(repo drone.Repo, build drone.Build, uri string, token string, opts scm.ListOptions) ([]string, error) { + newctx := context.Background() + + client, err := stash.New(uri) + if err != nil { + return nil, err + } + + client.Client = &http.Client{ + Transport: &transport.BearerToken{ + Token: token, + }, + } + + var got []*scm.Change + + if build.Before == "" || build.Before == scm.EmptyCommit { + got, _, err = client.Git.ListChanges(newctx, repo.Slug, build.After, opts) + if err != nil { + return nil, err + } + } else { + got, _, err = client.Git.CompareChanges(newctx, repo.Slug, build.Before, build.After, opts) + if err != nil { + return nil, err + } + } + + var files []string + for _, c := range got { + files = append(files, c.Path) + } + + return files, nil +} diff --git a/providers/stash_test.go b/providers/stash_test.go new file mode 100644 index 0000000..193d55d --- /dev/null +++ b/providers/stash_test.go @@ -0,0 +1,97 @@ +package providers + +import ( + "testing" + + "github.com/drone/drone-go/drone" + "github.com/drone/drone-go/plugin/converter" + "github.com/drone/go-scm/scm" + "github.com/google/go-cmp/cmp" + "github.com/h2non/gock" +) + +func TestGetStashFilesChangedCommit(t *testing.T) { + defer gock.Off() + + gock.New("http://example.com:7990"). + Get("/rest/api/1.0/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f/changes"). + MatchParam("limit", "30"). + Reply(200). + Type("application/json"). + File("testdata/stash/changes.json") + + req := &converter.Request{ + Build: drone.Build{ + Before: "", + After: "131cb13f4aed12e725177bc4b7c28db67839bf9f", + }, + Repo: drone.Repo{ + Namespace: "repos", + Name: "my-repo", + Slug: "PRJ/my-repo", + Config: ".drone.yml", + }, + } + + got, err := GetStashFilesChanged(req.Repo, req.Build, "http://example.com:7990", "invalidtoken", scm.ListOptions{Page: 1, Size: 30}) + if err != nil { + t.Error(err) + return + } + + want := []string{ + ".gitignore", + "COPYING", + "README.md", + "main.go", + } + + if diff := cmp.Diff(got, want); diff != "" { + t.Error("Unexpected Results") + t.Log(diff) + } +} + +func TestGetStashFilesChangedCompare(t *testing.T) { + defer gock.Off() + + gock.New("http://example.com:7990"). + Get("/rest/api/1.0/projects/PRJ/repos/my-repo/compare/changes"). + MatchParam("from", "4f4b0ef1714a5b6cafdaf2f53c7f5f5b38fb9348"). + MatchParam("to", "131cb13f4aed12e725177bc4b7c28db67839bf9f"). + MatchParam("limit", "30"). + Reply(200). + Type("application/json"). + File("testdata/stash/compare.json") + + req := &converter.Request{ + Build: drone.Build{ + Before: "4f4b0ef1714a5b6cafdaf2f53c7f5f5b38fb9348", + After: "131cb13f4aed12e725177bc4b7c28db67839bf9f", + }, + Repo: drone.Repo{ + Namespace: "repos", + Name: "my-repo", + Slug: "PRJ/my-repo", + Config: ".drone.yml", + }, + } + + got, err := GetStashFilesChanged(req.Repo, req.Build, "http://example.com:7990", "invalidtoken", scm.ListOptions{Page: 1, Size: 30}) + if err != nil { + t.Error(err) + return + } + + want := []string{ + ".gitignore", + "COPYING", + "README.md", + "main.go", + } + + if diff := cmp.Diff(got, want); diff != "" { + t.Error("Unexpected Results") + t.Log(diff) + } +} diff --git a/providers/testdata/stash/changes.json b/providers/testdata/stash/changes.json new file mode 100644 index 0000000..e1e66e1 --- /dev/null +++ b/providers/testdata/stash/changes.json @@ -0,0 +1,129 @@ +{ + "fromHash": null, + "toHash": "131cb13f4aed12e725177bc4b7c28db67839bf9f", + "properties": {}, + "values": [ + { + "contentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "fromContentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "path": { + "components": [ + ".gitignore" + ], + "parent": "", + "name": ".gitignore", + "extension": "gitignore", + "toString": ".gitignore" + }, + "percentUnchanged": -1, + "type": "DELETE", + "nodeType": "FILE", + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#.gitignore" + } + ] + }, + "properties": { + "gitChangeType": "DELETE" + } + }, + { + "contentId": "f1df0de7c4a94b7d83bd4864154e48e8b7f4d2cb", + "fromContentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "path": { + "components": [ + "COPYING" + ], + "parent": "", + "name": "COPYING", + "toString": "COPYING" + }, + "executable": false, + "percentUnchanged": -1, + "type": "MODIFY", + "nodeType": "FILE", + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#COPYING" + } + ] + }, + "properties": { + "gitChangeType": "MODIFY" + } + }, + { + "contentId": "e427984d4a2c1904681f2e2ee5980f37640d353f", + "fromContentId": "e427984d4a2c1904681f2e2ee5980f37640d353f", + "path": { + "components": [ + "README.md" + ], + "parent": "", + "name": "README.md", + "extension": "md", + "toString": "README.md" + }, + "executable": false, + "percentUnchanged": 100, + "type": "MOVE", + "nodeType": "FILE", + "srcPath": { + "components": [ + "README" + ], + "parent": "", + "name": "README", + "toString": "README" + }, + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#README.md" + } + ] + }, + "properties": { + "gitChangeType": "RENAME" + } + }, + { + "contentId": "002a5c740c7d288d40d490a43aae9297488a2433", + "fromContentId": "0000000000000000000000000000000000000000", + "path": { + "components": [ + "main.go" + ], + "parent": "", + "name": "main.go", + "extension": "go", + "toString": "main.go" + }, + "executable": false, + "percentUnchanged": -1, + "type": "ADD", + "nodeType": "FILE", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#main.go" + } + ] + }, + "properties": { + "gitChangeType": "ADD" + } + } + ], + "size": 4, + "isLastPage": true, + "start": 0, + "limit": 25, + "nextPageStart": null +} diff --git a/providers/testdata/stash/compare.json b/providers/testdata/stash/compare.json new file mode 100644 index 0000000..e82b695 --- /dev/null +++ b/providers/testdata/stash/compare.json @@ -0,0 +1,126 @@ +{ + "values": [ + { + "contentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "fromContentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "path": { + "components": [ + ".gitignore" + ], + "parent": "", + "name": ".gitignore", + "extension": "gitignore", + "toString": ".gitignore" + }, + "percentUnchanged": -1, + "type": "DELETE", + "nodeType": "FILE", + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#.gitignore" + } + ] + }, + "properties": { + "gitChangeType": "DELETE" + } + }, + { + "contentId": "f1df0de7c4a94b7d83bd4864154e48e8b7f4d2cb", + "fromContentId": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "path": { + "components": [ + "COPYING" + ], + "parent": "", + "name": "COPYING", + "toString": "COPYING" + }, + "executable": false, + "percentUnchanged": -1, + "type": "MODIFY", + "nodeType": "FILE", + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#COPYING" + } + ] + }, + "properties": { + "gitChangeType": "MODIFY" + } + }, + { + "contentId": "e427984d4a2c1904681f2e2ee5980f37640d353f", + "fromContentId": "e427984d4a2c1904681f2e2ee5980f37640d353f", + "path": { + "components": [ + "README.md" + ], + "parent": "", + "name": "README.md", + "extension": "md", + "toString": "README.md" + }, + "executable": false, + "percentUnchanged": 100, + "type": "MOVE", + "nodeType": "FILE", + "srcPath": { + "components": [ + "README" + ], + "parent": "", + "name": "README", + "toString": "README" + }, + "srcExecutable": false, + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#README.md" + } + ] + }, + "properties": { + "gitChangeType": "RENAME" + } + }, + { + "contentId": "002a5c740c7d288d40d490a43aae9297488a2433", + "fromContentId": "0000000000000000000000000000000000000000", + "path": { + "components": [ + "main.go" + ], + "parent": "", + "name": "main.go", + "extension": "go", + "toString": "main.go" + }, + "executable": false, + "percentUnchanged": -1, + "type": "ADD", + "nodeType": "FILE", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/commits/131cb13f4aed12e725177bc4b7c28db67839bf9f#main.go" + } + ] + }, + "properties": { + "gitChangeType": "ADD" + } + } + ], + "size": 4, + "isLastPage": true, + "start": 0, + "limit": 25, + "nextPageStart": null +} \ No newline at end of file