From 744cb00cdc8902fca21583aebf910fb35b2ba89e Mon Sep 17 00:00:00 2001 From: davidvader Date: Tue, 29 Oct 2024 15:36:35 -0500 Subject: [PATCH] enhance: new webhook tests --- compiler/native/environment_test.go | 6 +- constants/app_install.go | 4 + constants/event.go | 3 + database/step/table.go | 2 - database/types/repo_test.go | 48 ++--- .../testdata/hooks/installation_created.json | 100 +++++++++++ .../testdata/hooks/installation_deleted.json | 100 +++++++++++ .../installation_repositories_added.json | 103 +++++++++++ .../installation_repositories_removed.json | 103 +++++++++++ scm/github/webhook.go | 5 +- scm/github/webhook_test.go | 170 ++++++++++++++++++ 11 files changed, 614 insertions(+), 30 deletions(-) create mode 100644 scm/github/testdata/hooks/installation_created.json create mode 100644 scm/github/testdata/hooks/installation_deleted.json create mode 100644 scm/github/testdata/hooks/installation_repositories_added.json create mode 100644 scm/github/testdata/hooks/installation_repositories_removed.json diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index f988a1213..fc7717888 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -632,15 +632,15 @@ func TestNative_environment(t *testing.T) { netrc: &netrc, want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, - // todo: netrc + // netrc { w: workspace, b: &api.Build{ID: &num64, Repo: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, Number: &num, Parent: &num, Event: &deploy, Status: &str, Error: &str, Enqueued: &num64, Created: &num64, Started: &num64, Finished: &num64, Deploy: &target, Clone: &str, Source: &str, Title: &str, Message: &str, Commit: &str, Sender: &str, SenderSCMID: &str, Author: &str, Branch: &str, Ref: &pullref, BaseRef: &str}, m: &internal.Metadata{Database: &internal.Database{Driver: str, Host: str}, Queue: &internal.Queue{Channel: str, Driver: str, Host: str}, Source: &internal.Source{Driver: str, Host: str}, Vela: &internal.Vela{Address: str, WebAddress: str, OpenIDIssuer: str}}, r: &api.Repo{ID: &num64, Owner: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL}, u: &api.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - netrc: &netrc, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, + netrc: nil, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_SERVER_ADDR": "foo", "VELA_OPEN_ID_ISSUER": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SENDER_SCM_ID": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "TODO", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_OWNER": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_ID_TOKEN_REQUEST_URL": "foo/api/v1/repos/foo/builds/1/id_token"}, }, } diff --git a/constants/app_install.go b/constants/app_install.go index d9acf5f9a..34f146aad 100644 --- a/constants/app_install.go +++ b/constants/app_install.go @@ -25,6 +25,10 @@ const ( AppInstallRepositoriesSelectionAll = "all" // GitHub App install repositories selection when a subset of repositories are selected. AppInstallRepositoriesSelectionSelected = "selected" + // GitHub App install event type 'added'. + AppInstallRepositoriesAdded = "added" + // GitHub App install event type 'removed'. + AppInstallRepositoriesRemoved = "removed" ) const ( diff --git a/constants/event.go b/constants/event.go index ed5cd9224..a2ab76e3a 100644 --- a/constants/event.go +++ b/constants/event.go @@ -31,6 +31,9 @@ const ( // EventInstallation defines the event type for scm installation events. EventInstallation = "installation" + // EventInstallationRepositories defines the event type for scm installation_repositories events. + EventInstallationRepositories = "installation_repositories" + // Alternates for common user inputs that do not match our set constants. // EventPullAlternate defines the alternate event type for build and repo pull_request events. diff --git a/database/step/table.go b/database/step/table.go index 80ddf30ce..af7431c70 100644 --- a/database/step/table.go +++ b/database/step/table.go @@ -30,7 +30,6 @@ steps ( host VARCHAR(250), runtime VARCHAR(250), distribution VARCHAR(250), - check_id INTEGER, report_as VARCHAR(250), UNIQUE(build_id, number) ); @@ -57,7 +56,6 @@ steps ( host TEXT, runtime TEXT, distribution TEXT, - check_id INTEGER, report_as TEXT, UNIQUE(build_id, number) ); diff --git a/database/types/repo_test.go b/database/types/repo_test.go index 8bc3f2b68..f66d0b15f 100644 --- a/database/types/repo_test.go +++ b/database/types/repo_test.go @@ -321,38 +321,38 @@ func TestTypes_Repo_Validate(t *testing.T) { func TestTypes_RepoFromAPI(t *testing.T) { // setup types - repo := new(api.Repo) + r := new(api.Repo) owner := testutils.APIUser() owner.SetID(1) - repo.SetID(1) - repo.SetOwner(owner) - repo.SetHash("superSecretHash") - repo.SetOrg("github") - repo.SetName("octocat") - repo.SetFullName("github/octocat") - repo.SetLink("https://github.com/github/octocat") - repo.SetClone("https://github.com/github/octocat.git") - repo.SetBranch("main") - repo.SetTopics([]string{"cloud", "security"}) - repo.SetBuildLimit(10) - repo.SetTimeout(30) - repo.SetCounter(0) - repo.SetVisibility("public") - repo.SetPrivate(false) - repo.SetTrusted(false) - repo.SetActive(true) - repo.SetAllowEvents(api.NewEventsFromMask(1)) - repo.SetPipelineType("yaml") - repo.SetPreviousName("oldName") - repo.SetApproveBuild(constants.ApproveNever) - repo.SetInstallID(0) + r.SetID(1) + r.SetOwner(owner) + r.SetHash("superSecretHash") + r.SetOrg("github") + r.SetName("octocat") + r.SetFullName("github/octocat") + r.SetLink("https://github.com/github/octocat") + r.SetClone("https://github.com/github/octocat.git") + r.SetBranch("main") + r.SetTopics([]string{"cloud", "security"}) + r.SetBuildLimit(10) + r.SetTimeout(30) + r.SetCounter(0) + r.SetVisibility("public") + r.SetPrivate(false) + r.SetTrusted(false) + r.SetActive(true) + r.SetAllowEvents(api.NewEventsFromMask(1)) + r.SetPipelineType("yaml") + r.SetPreviousName("oldName") + r.SetApproveBuild(constants.ApproveNever) + r.SetInstallID(0) want := testRepo() want.Owner = User{} // run test - got := RepoFromAPI(repo) + got := RepoFromAPI(r) if diff := cmp.Diff(want, got); diff != "" { t.Errorf("RepoFromAPI() mismatch (-want +got):\n%s", diff) diff --git a/scm/github/testdata/hooks/installation_created.json b/scm/github/testdata/hooks/installation_created.json new file mode 100644 index 000000000..c3904b286 --- /dev/null +++ b/scm/github/testdata/hooks/installation_created.json @@ -0,0 +1,100 @@ +{ + "action": "created", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_deleted.json b/scm/github/testdata/hooks/installation_deleted.json new file mode 100644 index 000000000..9972e0cf9 --- /dev/null +++ b/scm/github/testdata/hooks/installation_deleted.json @@ -0,0 +1,100 @@ +{ + "action": "deleted", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_repositories_added.json b/scm/github/testdata/hooks/installation_repositories_added.json new file mode 100644 index 000000000..f75fedcd1 --- /dev/null +++ b/scm/github/testdata/hooks/installation_repositories_added.json @@ -0,0 +1,103 @@ +{ + "action": "added", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories_added": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "repositories_removed": [ + + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } + } \ No newline at end of file diff --git a/scm/github/testdata/hooks/installation_repositories_removed.json b/scm/github/testdata/hooks/installation_repositories_removed.json new file mode 100644 index 000000000..476193185 --- /dev/null +++ b/scm/github/testdata/hooks/installation_repositories_removed.json @@ -0,0 +1,103 @@ +{ + "action": "removed", + "installation": { + "id": 1, + "account": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "selected", + "access_tokens_url": "https://octocoders.github.io/api/v3/app/installations/1/access_tokens", + "repositories_url": "https://octocoders.github.io/api/v3/installation/repositories", + "html_url": "https://octocoders.github.io/settings/installations/1", + "app_id": 282, + "app_slug": "vela", + "target_id": 10919, + "target_type": "User", + "permissions": { + "checks": "write", + "contents": "read", + "metadata": "read" + }, + "events": [ + + ], + "created_at": "2024-10-22T08:50:39.000-05:00", + "updated_at": "2024-10-22T08:50:39.000-05:00", + "single_file_name": null, + "has_multiple_single_files": false, + "single_file_paths": [ + + ], + "suspended_by": null, + "suspended_at": null + }, + "repositories_added": [ + { + "id": 1, + "node_id": "MDEwOlJlcG9zaXRvcnkxMTg=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": true + }, + { + "id": 2, + "node_id": "MDEwOlJlcG9zaXRvcnk0MjI0MzE=", + "name": "Hello-World2", + "full_name": "Codertocat/Hello-World2", + "private": false + } + ], + "repositories_removed": [ + + ], + "requester": null, + "enterprise": { + "id": 1, + "slug": "github", + "name": "GitHub", + "node_id": "MDEwOkVudGVycHJpc2Ux", + "avatar_url": "https://octocoders.github.io/avatars/b/1?", + "description": null, + "website_url": null, + "html_url": "https://octocoders.github.io/businesses/github", + "created_at": "2018-10-24T21:19:19Z", + "updated_at": "2023-06-01T21:03:12Z" + }, + "sender": { + "login": "Codertocat", + "id": 4, + "node_id": "MDQ6VXNlcjQ=", + "avatar_url": "https://octocoders.github.io/avatars/u/4?", + "gravatar_id": "", + "url": "https://octocoders.github.io/api/v3/users/Codertocat", + "html_url": "https://octocoders.github.io/Codertocat", + "followers_url": "https://octocoders.github.io/api/v3/users/Codertocat/followers", + "following_url": "https://octocoders.github.io/api/v3/users/Codertocat/following{/other_user}", + "gists_url": "https://octocoders.github.io/api/v3/users/Codertocat/gists{/gist_id}", + "starred_url": "https://octocoders.github.io/api/v3/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://octocoders.github.io/api/v3/users/Codertocat/subscriptions", + "organizations_url": "https://octocoders.github.io/api/v3/users/Codertocat/orgs", + "repos_url": "https://octocoders.github.io/api/v3/users/Codertocat/repos", + "events_url": "https://octocoders.github.io/api/v3/users/Codertocat/events{/privacy}", + "received_events_url": "https://octocoders.github.io/api/v3/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } + } \ No newline at end of file diff --git a/scm/github/webhook.go b/scm/github/webhook.go index 51fa7638c..fee557e45 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -545,7 +545,7 @@ func (c *client) processRepositoryEvent(h *api.Hook, payload *github.RepositoryE // processInstallationEvent is a helper function to process the installation event. func (c *client) processInstallationEvent(_ context.Context, h *api.Hook, payload *github.InstallationEvent) (*internal.Webhook, error) { - h.SetEvent(constants.EventRepository) + h.SetEvent(constants.EventInstallation) h.SetEventAction(payload.GetAction()) install := new(internal.Installation) @@ -573,6 +573,9 @@ func (c *client) processInstallationEvent(_ context.Context, h *api.Hook, payloa // processInstallationRepositoriesEvent is a helper function to process the installation repositories event. func (c *client) processInstallationRepositoriesEvent(_ context.Context, h *api.Hook, payload *github.InstallationRepositoriesEvent) (*internal.Webhook, error) { + h.SetEvent(constants.EventInstallationRepositories) + h.SetEventAction(payload.GetAction()) + install := new(internal.Installation) install.Action = payload.GetAction() diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index 8f7f5bf06..3867e1a86 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -1555,3 +1555,173 @@ func TestGithub_GetDeliveryID(t *testing.T) { t.Errorf("getDeliveryID returned: %v; want: %v", got, want) } } + +func TestGitHub_ProcessWebhook_Installation(t *testing.T) { + // setup tests + var createdHook api.Hook + createdHook.SetNumber(1) + createdHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") + createdHook.SetWebhookID(123456) + createdHook.SetCreated(time.Now().UTC().Unix()) + createdHook.SetHost("github.com") + createdHook.SetEvent(constants.EventInstallation) + createdHook.SetEventAction(constants.AppInstallCreated) + createdHook.SetStatus(constants.StatusSuccess) + + deletedHook := createdHook + deletedHook.SetEventAction(constants.AppInstallDeleted) + + tests := []struct { + name string + file string + wantHook *api.Hook + wantInstall *internal.Installation + }{ + { + name: "installation created", + file: "testdata/hooks/installation_created.json", + wantHook: &createdHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallCreated, + ID: 1, + RepositoriesAdded: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + { + name: "installation deleted", + file: "testdata/hooks/installation_deleted.json", + wantHook: &deletedHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallDeleted, + ID: 1, + RepositoriesRemoved: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + } + + // setup router + s := httptest.NewServer(http.NotFoundHandler()) + defer s.Close() + + for _, tt := range tests { + // setup request + body, err := os.Open(tt.file) + if err != nil { + t.Errorf("unable to open file: %v", err) + } + + defer body.Close() + + request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", body) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("User-Agent", "GitHub-Hookshot/a22606a") + request.Header.Set("X-GitHub-Delivery", "7bd477e4-4415-11e9-9359-0d41fdf9567e") + request.Header.Set("X-GitHub-Hook-ID", "123456") + request.Header.Set("X-GitHub-Event", "installation") + + // setup client + client, _ := NewTest(s.URL) + + want := &internal.Webhook{ + Hook: tt.wantHook, + Installation: tt.wantInstall, + } + + got, err := client.ProcessWebhook(context.TODO(), request) + + if err != nil { + t.Errorf("ProcessWebhook returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) + } + } +} + +func TestGitHub_ProcessWebhook_InstallationRepositories(t *testing.T) { + // setup tests + var reposAddedHook api.Hook + reposAddedHook.SetNumber(1) + reposAddedHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") + reposAddedHook.SetWebhookID(123456) + reposAddedHook.SetCreated(time.Now().UTC().Unix()) + reposAddedHook.SetHost("github.com") + reposAddedHook.SetEvent(constants.EventInstallationRepositories) + reposAddedHook.SetEventAction(constants.AppInstallRepositoriesAdded) + reposAddedHook.SetStatus(constants.StatusSuccess) + + reposRemovedHook := reposAddedHook + reposRemovedHook.SetEventAction(constants.AppInstallRepositoriesRemoved) + + tests := []struct { + name string + file string + wantHook *api.Hook + wantInstall *internal.Installation + }{ + { + name: "installation_repositories repos added", + file: "testdata/hooks/installation_repositories_added.json", + wantHook: &reposAddedHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallRepositoriesAdded, + ID: 1, + RepositoriesAdded: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + { + name: "installation_repositories repos removed", + file: "testdata/hooks/installation_repositories_removed.json", + wantHook: &reposRemovedHook, + wantInstall: &internal.Installation{ + Action: constants.AppInstallRepositoriesRemoved, + ID: 1, + RepositoriesRemoved: []string{"Hello-World", "Hello-World2"}, + Org: "Codertocat", + }, + }, + } + + // setup router + s := httptest.NewServer(http.NotFoundHandler()) + defer s.Close() + + for _, tt := range tests { + // setup request + body, err := os.Open(tt.file) + if err != nil { + t.Errorf("unable to open file: %v", err) + } + + defer body.Close() + + request, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/test", body) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("User-Agent", "GitHub-Hookshot/a22606a") + request.Header.Set("X-GitHub-Delivery", "7bd477e4-4415-11e9-9359-0d41fdf9567e") + request.Header.Set("X-GitHub-Hook-ID", "123456") + request.Header.Set("X-GitHub-Event", "installation_repositories") + + // setup client + client, _ := NewTest(s.URL) + + want := &internal.Webhook{ + Hook: tt.wantHook, + Installation: tt.wantInstall, + } + + got, err := client.ProcessWebhook(context.TODO(), request) + + if err != nil { + t.Errorf("ProcessWebhook returned err: %v", err) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ProcessWebhook() mismatch (-want +got):\n%s", diff) + } + } +}