Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#19314, #15700): allow ssh/altssh subdomains in repo URLs to match webhook payload #21227

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
671ed99
allow ssh/altssh subdomains in repo URLs to match webhook payload
mtbennett-godaddy Dec 17, 2024
750c0a9
Merge branch 'master' into fix-19314
mtbennett-godaddy Dec 17, 2024
71d126f
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 19, 2024
5ed35ea
add affected app sets to recently merged test
mtbennett-godaddy Dec 19, 2024
2f46cc4
Merge branch 'fix-19314' of github.com:mtbennett-godaddy/argo-cd into…
mtbennett-godaddy Dec 19, 2024
453ad46
add tests
mtbennett-godaddy Dec 19, 2024
9678a96
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 20, 2024
b5647ce
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 20, 2024
1312173
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 23, 2024
7ba7cb3
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 30, 2024
c8c7abd
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 30, 2024
5e7658c
more gitlab -> gitlab.com tweaks in test data
mtbennett-godaddy Dec 30, 2024
eb518a1
use word and digit character classes to simplify regexes a bit
mtbennett-godaddy Dec 30, 2024
5131354
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Dec 31, 2024
2b9d884
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Jan 2, 2025
a3c50a5
Merge branch 'master' of github.com:argoproj/argo-cd into fix-19314
mtbennett-godaddy Jan 3, 2025
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
24 changes: 12 additions & 12 deletions applicationset/webhook/testdata/gitlab-event.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@
"id": 1,
"name": "project",
"description": "",
"web_url": "https://gitlab/group/name",
"web_url": "https://gitlab.com/group/name",
"avatar_url": null,
"git_ssh_url": "ssh://git@gitlab:2222/group/name.git",
"git_http_url": "https://gitlab/group/name.git",
"git_ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
"git_http_url": "https://gitlab.com/group/name.git",
"namespace": "group",
"visibility_level": 1,
"path_with_namespace": "group/name",
"default_branch": "master",
"ci_config_path": null,
"homepage": "https://gitlab/group/name",
"url": "ssh://git@gitlab:2222/group/name.git",
"ssh_url": "ssh://git@gitlab:2222/group/name.git",
"http_url": "https://gitlab/group/name.git"
"homepage": "https://gitlab.com/group/name",
"url": "ssh://git@gitlab.com:2222/group/name.git",
"ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
"http_url": "https://gitlab.com/group/name.git"
},
"commits": [
{
"id": "bb0748feaa336d841c251017e4e374c22d0c8a98",
"message": "Test commit message\n",
"timestamp": "2020-01-06T03:47:55Z",
"url": "https://gitlab/group/name/commit/bb0748feaa336d841c251017e4e374c22d0c8a98",
"url": "https://gitlab.com/group/name/commit/bb0748feaa336d841c251017e4e374c22d0c8a98",
"author": {
"name": "User",
"email": "[email protected]"
Expand All @@ -55,11 +55,11 @@
},
"repository": {
"name": "name",
"url": "ssh://git@gitlab:2222/group/name.git",
"url": "ssh://git@gitlab.com:2222/group/name.git",
"description": "",
"homepage": "https://gitlab/group/name",
"git_http_url": "https://gitlab/group/name.git",
"git_ssh_url": "ssh://git@gitlab:2222/group/name.git",
"homepage": "https://gitlab.com/group/name",
"git_http_url": "https://gitlab.com/group/name.git",
"git_ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
mtbennett-godaddy marked this conversation as resolved.
Show resolved Hide resolved
"visibility_level": 10
}
}
16 changes: 2 additions & 14 deletions applicationset/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,7 @@ func getGitGeneratorInfo(payload interface{}) *gitGeneratorInfo {
}

log.Infof("Received push event repo: %s, revision: %s, touchedHead: %v", webURL, revision, touchedHead)
urlObj, err := url.Parse(webURL)
if err != nil {
log.Errorf("Failed to parse repoURL '%s'", webURL)
return nil
}
regexpStr := `(?i)(http://|https://|\w+@|ssh://(\w+@)?)` + urlObj.Hostname() + "(:[0-9]+|)[:/]" + urlObj.Path[1:] + "(\\.git)?$"
repoRegexp, err := regexp.Compile(regexpStr)
repoRegexp, err := webhook.GetWebUrlRegex(webURL)
if err != nil {
log.Errorf("Failed to compile regexp for repoURL '%s'", webURL)
return nil
Expand All @@ -245,13 +239,7 @@ func getPRGeneratorInfo(payload interface{}) *prGeneratorInfo {
}

apiURL := payload.Repository.URL
urlObj, err := url.Parse(apiURL)
if err != nil {
log.Errorf("Failed to parse repoURL '%s'", apiURL)
return nil
}
regexpStr := `(?i)(http://|https://|\w+@|ssh://(\w+@)?)` + urlObj.Hostname() + "(:[0-9]+|)[:/]"
apiRegexp, err := regexp.Compile(regexpStr)
apiRegexp, err := webhook.GetApiUrlRegex(apiURL)
if err != nil {
log.Errorf("Failed to compile regexp for repoURL '%s'", apiURL)
return nil
Expand Down
20 changes: 12 additions & 8 deletions applicationset/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-GitHub-Event",
headerValue: "push",
payloadFile: "github-commit-event.json",
effectedAppSets: []string{"git-github", "matrix-git-github", "merge-git-github", "matrix-scm-git-github", "matrix-nested-git-github", "merge-nested-git-github", "plugin", "matrix-pull-request-github-plugin"},
effectedAppSets: []string{"git-github", "git-github-ssh", "git-github-alt-ssh", "matrix-git-github", "merge-git-github", "matrix-scm-git-github", "matrix-nested-git-github", "merge-nested-git-github", "plugin", "matrix-pull-request-github-plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: true,
},
Expand All @@ -81,7 +81,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-GitHub-Event",
headerValue: "push",
payloadFile: "github-commit-branch-event.json",
effectedAppSets: []string{"git-github", "plugin", "matrix-pull-request-github-plugin"},
effectedAppSets: []string{"git-github", "git-github-ssh", "git-github-alt-ssh", "plugin", "matrix-pull-request-github-plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: true,
},
Expand All @@ -90,7 +90,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-GitHub-Event",
headerValue: "ping",
payloadFile: "github-ping-event.json",
effectedAppSets: []string{"git-github", "plugin"},
effectedAppSets: []string{"git-github", "git-github-ssh", "git-github-alt-ssh", "plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: false,
},
Expand All @@ -99,7 +99,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-Gitlab-Event",
headerValue: "Push Hook",
payloadFile: "gitlab-event.json",
effectedAppSets: []string{"git-gitlab", "plugin", "matrix-pull-request-github-plugin"},
effectedAppSets: []string{"git-gitlab", "git-gitlab-ssh", "git-gitlab-alt-ssh", "plugin", "matrix-pull-request-github-plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: true,
},
Expand All @@ -108,7 +108,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-Gitlab-Event",
headerValue: "System Hook",
payloadFile: "gitlab-event.json",
effectedAppSets: []string{"git-gitlab", "plugin", "matrix-pull-request-github-plugin"},
effectedAppSets: []string{"git-gitlab", "git-gitlab-ssh", "git-gitlab-alt-ssh", "plugin", "matrix-pull-request-github-plugin"},
expectedStatusCode: http.StatusOK,
expectedRefresh: true,
},
Expand All @@ -117,7 +117,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-Random-Event",
headerValue: "Push Hook",
payloadFile: "gitlab-event.json",
effectedAppSets: []string{"git-gitlab", "plugin"},
effectedAppSets: []string{"git-gitlab", "git-gitlab-ssh", "git-gitlab-alt-ssh", "plugin"},
expectedStatusCode: http.StatusBadRequest,
expectedRefresh: false,
},
Expand All @@ -126,7 +126,7 @@ func TestWebhookHandler(t *testing.T) {
headerKey: "X-Random-Event",
headerValue: "Push Hook",
payloadFile: "invalid-event.json",
effectedAppSets: []string{"git-gitlab", "plugin"},
effectedAppSets: []string{"git-gitlab", "git-gitlab-ssh", "git-gitlab-alt-ssh", "plugin"},
expectedStatusCode: http.StatusBadRequest,
expectedRefresh: false,
},
Expand Down Expand Up @@ -209,7 +209,11 @@ func TestWebhookHandler(t *testing.T) {
fc := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"),
fakeAppWithGitGenerator("git-github-copy", namespace, "https://github.com/org/repo-copy"),
fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"),
fakeAppWithGitGenerator("git-github-ssh", namespace, "ssh://[email protected]/org/repo"),
fakeAppWithGitGenerator("git-github-alt-ssh", namespace, "ssh://[email protected]:443/org/repo"),
fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab.com/group/name"),
fakeAppWithGitGenerator("git-gitlab-ssh", namespace, "ssh://[email protected]/group/name"),
fakeAppWithGitGenerator("git-gitlab-alt-ssh", namespace, "ssh://[email protected]:443/group/name"),
fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"),
fakeAppWithGitGeneratorWithRevision("github-shorthand", namespace, "https://github.com/org/repo", "env/dev"),
fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "CodErTOcat", "Hello-World"),
Expand Down
24 changes: 12 additions & 12 deletions util/webhook/testdata/gitlab-event.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@
"id": 1,
"name": "project",
"description": "",
"web_url": "https://gitlab/group/name",
"web_url": "https://gitlab.com/group/name",
"avatar_url": null,
"git_ssh_url": "ssh://git@gitlab:2222/group/name.git",
"git_http_url": "https://gitlab/group/name.git",
"git_ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
"git_http_url": "https://gitlab.com/group/name.git",
"namespace": "group",
"visibility_level": 1,
"path_with_namespace": "group/name",
"default_branch": "master",
"ci_config_path": null,
"homepage": "https://gitlab/group/name",
"url": "ssh://git@gitlab:2222/group/name.git",
"ssh_url": "ssh://git@gitlab:2222/group/name.git",
"http_url": "https://gitlab/group/name.git"
"homepage": "https://gitlab.com/group/name",
"url": "ssh://git@gitlab.com:2222/group/name.git",
"ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
"http_url": "https://gitlab.com/group/name.git"
},
"commits": [
{
"id": "bb0748feaa336d841c251017e4e374c22d0c8a98",
"message": "Test commit message\n",
"timestamp": "2020-01-06T03:47:55Z",
"url": "https://gitlab/group/name/commit/bb0748feaa336d841c251017e4e374c22d0c8a98",
"url": "https://gitlab.com/group/name/commit/bb0748feaa336d841c251017e4e374c22d0c8a98",
"author": {
"name": "User",
"email": "[email protected]"
Expand All @@ -55,11 +55,11 @@
},
"repository": {
"name": "name",
"url": "ssh://git@gitlab:2222/group/name.git",
"url": "ssh://git@gitlab.com:2222/group/name.git",
"description": "",
"homepage": "https://gitlab/group/name",
"git_http_url": "https://gitlab/group/name.git",
"git_ssh_url": "ssh://git@gitlab:2222/group/name.git",
"homepage": "https://gitlab.com/group/name",
"git_http_url": "https://gitlab.com/group/name.git",
"git_ssh_url": "ssh://git@gitlab.com:2222/group/name.git",
"visibility_level": 10
}
}
37 changes: 29 additions & 8 deletions util/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload interface{}) {
}

for _, webURL := range webURLs {
repoRegexp, err := getWebUrlRegex(webURL)
repoRegexp, err := GetWebUrlRegex(webURL)
if err != nil {
log.Warnf("Failed to get repoRegexp: %s", err)
continue
Expand Down Expand Up @@ -329,21 +329,42 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload interface{}) {
}
}

// getWebUrlRegex compiles a regex that will match any targetRevision referring to the same repo as the given webURL.
// GetWebUrlRegex compiles a regex that will match any targetRevision referring to the same repo as the given webURL.
// webURL is expected to be a URL from an SCM webhook payload pointing to the web page for the repo.
func getWebUrlRegex(webURL string) (*regexp.Regexp, error) {
urlObj, err := url.Parse(webURL)
func GetWebUrlRegex(webURL string) (*regexp.Regexp, error) {
// 1. Optional: protocol (`http`, `https`, or `ssh`) followed by `://`
// 2. Optional: username followed by `@`
// 3. Optional: `ssh` or `altssh` subdomain
// 4. Required: hostname parsed from `webURL`
// 5. Optional: `:` followed by port number
// 6. Required: `:` or `/`
// 7. Required: path parsed from `webURL`
// 8. Optional: `.git` extension
return getUrlRegex(webURL, `(?i)^((https?|ssh)://)?(%[1]s@)?((alt)?ssh\.)?%[2]s(:[0-9]+)?[:/]%[3]s(\.git)?$`)
}

// GetApiUrlRegex compiles a regex that will match any targetRevision referring to the same repo as the given apiURL.
func GetApiUrlRegex(apiURL string) (*regexp.Regexp, error) {
// 1. Optional: protocol (`http` or `https`) followed by `://`
// 2. Optional: username followed by `@`
// 3. Required: hostname parsed from `webURL`
// 4. Optional: `:` followed by port number
// 5. Optional: `/`
return getUrlRegex(apiURL, `(?i)^(https?://)?(%[1]s@)?%[2]s(:[0-9]+)?/?$`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These regexes seem different from the previous ones.

Copy link
Author

@mtbennett-godaddy mtbennett-godaddy Dec 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, there are subtle differences.

The previous Application webhook web URL regex was:

(http://|https://|%s@|ssh://(%s@)?)%s(:[0-9]+|)[:/]%s(\.git)?

Which broke down to:

  1. http://, https://, username@, ssh://, or ssh://username@ (where username is the RFC 3986 pattern defined elsewhere in the file)
  2. hostname from original URL, passed through regexp.QuoteMeta()
  3. :port or nothing
  4. colon or slash
  5. path from original URL, passed through regexp.QuoteMeta() (which might be empty)
  6. optional .git extension

The previous ApplicationSet webhook web URL regex was:

(http://|https://|\w+@|ssh://(\w+@)?)%s(:[0-9]+|)[:/]%s(\.git)?

Which differed from the Application one in that:

  • Any sequence of one or more word characters was allowed for username.
  • The hostname and path values were not passed through regexp.QuoteMeta().

In the new common web URL regex used by both webhooks:

The previous ApplicationSet webhook API URL regex was:

(http://|https://|\w+@|ssh://(\w+@)?)%s(:[0-9]+|)[:/]

(Basically the same as the web URL regex, but no path or extension.)

The new API URL regex has the same benefits as listed above for the new web URL regex (except for the altssh/ssh subdomain allowance since these URLs will never have that).

}

func getUrlRegex(originalURL string, regexpFormat string) (*regexp.Regexp, error) {
urlObj, err := url.Parse(originalURL)
if err != nil {
return nil, fmt.Errorf("failed to parse repoURL '%s'", webURL)
return nil, fmt.Errorf("failed to parse URL '%s'", originalURL)
}

regexEscapedHostname := regexp.QuoteMeta(urlObj.Hostname())
regexEscapedPath := regexp.QuoteMeta(urlObj.EscapedPath()[1:])
regexpStr := fmt.Sprintf(`(?i)^(http://|https://|%s@|ssh://(%s@)?)%s(:[0-9]+|)[:/]%s(\.git)?$`,
usernameRegex, usernameRegex, regexEscapedHostname, regexEscapedPath)
regexpStr := fmt.Sprintf(regexpFormat, usernameRegex, regexEscapedHostname, regexEscapedPath)
repoRegexp, err := regexp.Compile(regexpStr)
if err != nil {
return nil, fmt.Errorf("failed to compile regexp for repoURL '%s'", webURL)
return nil, fmt.Errorf("failed to compile regexp for URL '%s'", originalURL)
}

return repoRegexp, nil
Expand Down
61 changes: 56 additions & 5 deletions util/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func TestGitLabPushEvent(t *testing.T) {
close(h.queue)
h.Wait()
assert.Equal(t, http.StatusOK, w.Code)
expectedLogResult := "Received push event repo: https://gitlab/group/name, revision: master, touchedHead: true"
expectedLogResult := "Received push event repo: https://gitlab.com/group/name, revision: master, touchedHead: true"
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
hook.Reset()
}
Expand All @@ -397,7 +397,7 @@ func TestGitLabSystemEvent(t *testing.T) {
close(h.queue)
h.Wait()
assert.Equal(t, http.StatusOK, w.Code)
expectedLogResult := "Received push event repo: https://gitlab/group/name, revision: master, touchedHead: true"
expectedLogResult := "Received push event repo: https://gitlab.com/group/name, revision: master, touchedHead: true"
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
hook.Reset()
}
Expand Down Expand Up @@ -604,7 +604,7 @@ func Test_affectedRevisionInfo_appRevisionHasChanged(t *testing.T) {
}
}

func Test_getWebUrlRegex(t *testing.T) {
func Test_GetWebUrlRegex(t *testing.T) {
tests := []struct {
shouldMatch bool
webURL string
Expand All @@ -620,26 +620,77 @@ func Test_getWebUrlRegex(t *testing.T) {
{false, "https://example.com/org/repo", "https://example.com/org/repo-2", "partial match should not match"},
{true, "https://example.com/org/repo", "https://example.com/org/repo.git", "no .git should match with .git"},
{true, "https://example.com/org/repo", "[email protected]:org/repo", "git without protocol should match"},
{true, "https://example.com/org/repo", "[email protected]:org/repo", "git with non-git username shout match"},
{true, "https://example.com/org/repo", "[email protected]:org/repo", "git with non-git username should match"},
{true, "https://example.com/org/repo", "ssh://[email protected]/org/repo", "git with protocol should match"},
{true, "https://example.com/org/repo", "ssh://[email protected]:22/org/repo", "git with port number should match"},
{true, "https://example.com:443/org/repo", "ssh://[email protected]:22/org/repo", "https and ssh w/ different port numbers should match"},
{true, "https://example.com:443/org/repo", "ssh://[email protected]:443/org/repo", "https and ssh w/ ssh subdomain should match"},
{true, "https://example.com:443/org/repo", "ssh://[email protected]:443/org/repo", "https and ssh w/ altssh subdomain should match"},
{false, "https://example.com:443/org/repo", "ssh://[email protected]:443/org/repo", "https and ssh w/ unknown subdomain should not match"},
{true, "https://example.com/org/repo", "ssh://[email protected]/org/repo", "valid usernames with hyphens in repo should match"},
{false, "https://example.com/org/repo", "ssh://[email protected]/org/repo", "invalid usernames with hyphens in repo should not match"},
{true, "https://example.com:443/org/repo", "[email protected]:22:ORG/REPO", "matches aren't case-sensitive"},
{true, "https://example.com/org/repo%20", "https://example.com/org/repo%20", "escape codes in path are preserved"},
{true, "https://[email protected]/org/repo", "http://example.com/org/repo", "https+username should match http"},
{true, "https://[email protected]/org/repo", "https://example.com/org/repo", "https+username should match https"},
{true, "http://example.com/org/repo", "https://[email protected]/org/repo", "http should match https+username"},
{true, "https://example.com/org/repo", "https://[email protected]/org/repo", "https should match https+username"},
{true, "https://[email protected]/org/repo", "ssh://example.com/org/repo", "https+username should match ssh"},
}

for _, testCase := range tests {
testCopy := testCase
t.Run(testCopy.name, func(t *testing.T) {
t.Parallel()
regexp, err := getWebUrlRegex(testCopy.webURL)
regexp, err := GetWebUrlRegex(testCopy.webURL)
require.NoError(t, err)
if matches := regexp.MatchString(testCopy.repo); matches != testCopy.shouldMatch {
t.Errorf("sourceRevisionHasChanged() = %v, want %v", matches, testCopy.shouldMatch)
}
})
}

t.Run("bad URL should error", func(t *testing.T) {
_, err := GetWebUrlRegex("%%")
require.Error(t, err)
})
}

func Test_GetApiUrlRegex(t *testing.T) {
tests := []struct {
shouldMatch bool
apiURL string
repo string
name string
}{
// Ensure input is regex-escaped.
{false, "https://an.example.com/", "https://an-example.com/", "dots in domain names should not be treated as wildcards"},

// Standard cases.
{true, "https://example.com/", "https://example.com/", "exact match should match"},
{false, "https://example.com/", "ssh://example.com/", "should not match ssh"},
{true, "https://[email protected]/", "http://example.com/", "https+username should match http"},
{true, "https://[email protected]/", "https://example.com/", "https+username should match https"},
{true, "http://example.com/", "https://[email protected]/", "http should match https+username"},
{true, "https://example.com/", "https://[email protected]/", "https should match https+username"},
}

for _, testCase := range tests {
testCopy := testCase
t.Run(testCopy.name, func(t *testing.T) {
t.Parallel()
regexp, err := GetApiUrlRegex(testCopy.apiURL)
require.NoError(t, err)
if matches := regexp.MatchString(testCopy.repo); matches != testCopy.shouldMatch {
t.Errorf("sourceRevisionHasChanged() = %v, want %v", matches, testCopy.shouldMatch)
}
})
}

t.Run("bad URL should error", func(t *testing.T) {
_, err := GetApiUrlRegex("%%")
require.Error(t, err)
})
}

func TestGitHubCommitEventMaxPayloadSize(t *testing.T) {
Expand Down
Loading