Repository: runatlantis/atlantis (language: go)

Issue:
Atlantis does not take into account GitHub checks in the `Expected - Waiting for status to be reported`-state when determining if a pull request is mergeable
<!--- Please keep this note for the community --->

### Community Note

* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request. Searching for pre-existing feature requests helps us consolidate datapoints for identical requirements into a single place, thank you!
* Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request.
* If you are interested in working on this issue or have submitted a pull request, please leave a comment.

<!--- Thank you for keeping this note for the community --->

---

<!---
When filing a bug, please include the following headings if possible.
Any example text in this template can be deleted.
--->

### Overview of the Issue

<!---
Please describe the issue you are having and how you encountered the problem.
--->

Atlantis does not take into account GitHub checks in the `Expected - Waiting for status to be reported`-state when determining if a pull request is mergeable. Resolving this issue may however be infeasible given the current state of GitHub's API, see https://github.com/cli/cli/issues/6448. This is really an issue with GitHub's API more than with Atlantis, but I'm putting this issue up anyway for the sake of visibility. It may be possible to list all required checks through the rulesets and branch protection endpoints of the API and check that all of them appear in the status checks rollup of the pull request, but this could get complicated and is essentially reimplementing GitHub's internal logic.


### Reproduction Steps

<!--- 
In order to effectively and quickly resolve the issue, please provide exact steps that allow us the reproduce the problem. If no steps are provided, then it will likely take longer to get the issue resolved.
--->

Creating a branch protection rule or ruleset that requires a check that does not exist will result in a required check forever stuck in the `Expected - Waiting for status to be reported`-state. The pull request will not be mergeable in this condition. The GitHub GUI will show this, being rendered internally in GitHub, but the GitHub API will not expose it. Consequently Atlantis will not know of the required check and will assume the pull request is mergeable.

A similar situation can occur if a new required check is added through branch protection or ruleset after a commit was pushed to the pull request. The creation of the check does not trigger its execution, so the check will be stuck in the `Expected - Waiting for status to be reported`-state. Again, this will be shown in the GitHub GUI, but will not be exposed in the GitHub API, the pull request is not mergeable but Atlantis will not know. But in this case, pushing a new commit on the pull request branch or closing and re-opening the pull request will trigger the check (and it will go into one of the states visible in the API, e.g. `PENDING`), after which it will in fact be visible to Atlantis as expected.


### Logs

<!---
Provide log files from Atlantis server

logs can be retrieved from the deployment or from atlantis comments by adding `--debug` such as `atlantis plan --debug`

<details>
  <summary>Logs</summary>

```
log output
```

</details>
--->


### Environment details

<!---
If not already included, please provide the following:

- Atlantis version:
- Deployment method: ecs/eks/helm/tf module
- If not running the latest Atlantis version have you tried to reproduce this issue on the latest version: 
- Atlantis flags:

Atlantis server-side config file:

```yaml
# config file
```

Repo `atlantis.yaml` file:

```yaml
# config file
```

Any other information you can provide about the environment/deployment (efs/nfs, aws/gcp, k8s/fargate, etc)
--->


### Additional Context

<!---
Additional context on the problem. Docs, links to blogs, or other material that lead you to discover this issue or were helpful in troubleshooting the issue. 

Use a bulleted list to link to tickets
--->




Discussion / hints:

Fix patch:
diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go
index a561833e6f..040a85958b 100644
--- a/server/events/vcs/github_client.go
+++ b/server/events/vcs/github_client.go
@@ -18,6 +18,7 @@ import (
 	"encoding/base64"
 	"fmt"
 	"net/http"
+	"strconv"
 	"strings"
 	"time"
 
@@ -39,6 +40,40 @@ var (
 	pullRequestDismissalMessage = *githubv4.NewString("Dismissing reviews because of plan changes")
 )
 
+type GithubRepoIdCacheEntry struct {
+	RepoId     githubv4.Int
+	LookupTime time.Time
+}
+
+type GitHubRepoIdCache struct {
+	cache map[githubv4.String]GithubRepoIdCacheEntry
+}
+
+func NewGitHubRepoIdCache() GitHubRepoIdCache {
+	return GitHubRepoIdCache{
+		cache: make(map[githubv4.String]GithubRepoIdCacheEntry),
+	}
+}
+
+func (c *GitHubRepoIdCache) Get(key githubv4.String) (githubv4.Int, bool) {
+	entry, ok := c.cache[key]
+	if !ok {
+		return githubv4.Int(0), false
+	}
+	if time.Since(entry.LookupTime) > time.Hour {
+		delete(c.cache, key)
+		return githubv4.Int(0), false
+	}
+	return entry.RepoId, true
+}
+
+func (c *GitHubRepoIdCache) Set(key githubv4.String, value githubv4.Int) {
+	c.cache[key] = GithubRepoIdCacheEntry{
+		RepoId:     value,
+		LookupTime: time.Now(),
+	}
+}
+
 // GithubClient is used to perform GitHub actions.
 type GithubClient struct {
 	user                  string
@@ -47,6 +82,7 @@ type GithubClient struct {
 	ctx                   context.Context
 	config                GithubConfig
 	maxCommentsPerCommand int
+	repoIdCache           GitHubRepoIdCache
 }
 
 // GithubAppTemporarySecrets holds app credentials obtained from github after creation.
@@ -109,6 +145,7 @@ func NewGithubClient(hostname string, credentials GithubCredentials, config Gith
 	if err != nil {
 		return nil, errors.Wrap(err, "getting user")
 	}
+
 	return &GithubClient{
 		user:                  user,
 		client:                client,
@@ -116,6 +153,7 @@ func NewGithubClient(hostname string, credentials GithubCredentials, config Gith
 		ctx:                   context.Background(),
 		config:                config,
 		maxCommentsPerCommand: maxCommentsPerCommand,
+		repoIdCache:           NewGitHubRepoIdCache(),
 	}, nil
 }
 
@@ -410,119 +448,348 @@ func (g *GithubClient) DiscardReviews(repo models.Repo, pull models.PullRequest)
 	return nil
 }
 
-// isRequiredCheck is a helper function to determine if a check is required or not
-func isRequiredCheck(check string, required []string) bool {
-	//in go1.18 can prob replace this with slices.Contains
-	for _, r := range required {
-		if r == check {
-			return true
-		}
-	}
+type PageInfo struct {
+	EndCursor   *githubv4.String
+	HasNextPage githubv4.Boolean
+}
 
-	return false
+type WorkflowFileReference struct {
+	Path         githubv4.String
+	RepositoryId githubv4.Int
+	Sha          *githubv4.String
 }
 
-// GetCombinedStatusMinusApply checks Statuses for PR, excluding atlantis apply. Returns true if all other statuses are not in failure.
-func (g *GithubClient) GetCombinedStatusMinusApply(logger logging.SimpleLogging, repo models.Repo, pull *github.PullRequest, vcstatusname string) (bool, error) {
-	logger.Debug("Checking if GitHub pull request %d has successful status checks", pull.GetNumber())
-	//check combined status api
-	status, resp, err := g.client.Repositories.GetCombinedStatus(g.ctx, *pull.Head.Repo.Owner.Login, repo.Name, *pull.Head.Ref, nil)
-	if resp != nil {
-		logger.Debug("GET /repos/%v/%v/commits/%s/status returned: %v", *pull.Head.Repo.Owner.Login, repo.Name, *pull.Head.Ref, resp.StatusCode)
+func (original WorkflowFileReference) Copy() WorkflowFileReference {
+	copy := WorkflowFileReference{
+		Path:         original.Path,
+		RepositoryId: original.RepositoryId,
+		Sha:          new(githubv4.String),
 	}
-	if err != nil {
-		return false, errors.Wrap(err, "getting combined status")
+	if original.Sha != nil {
+		*copy.Sha = *original.Sha
 	}
+	return copy
+}
 
-	//iterate over statuses - return false if we find one that isn't "apply" and doesn't have state = "success"
-	for _, r := range status.Statuses {
-		if strings.HasPrefix(*r.Context, fmt.Sprintf("%s/%s", vcstatusname, command.Apply.String())) {
-			continue
-		}
-		if *r.State != "success" {
-			return false, nil
-		}
+type WorkflowRun struct {
+	File struct {
+		Path              githubv4.String
+		RepositoryFileUrl githubv4.String
+		RepositoryName    githubv4.String
 	}
+}
 
-	//get required status checks
-	required, resp, err := g.client.Repositories.GetBranchProtection(context.Background(), repo.Owner, repo.Name, *pull.Base.Ref)
-	if resp != nil {
-		logger.Debug("GET /repos/%v/%v/branches/%s/protection returned: %v", repo.Owner, repo.Name, *pull.Base.Ref, resp.StatusCode)
+type CheckRun struct {
+	Name       githubv4.String
+	Conclusion githubv4.String
+	// Not currently used: GitHub API classifies as required if coming from ruleset, even when the ruleset is not enforced!
+	IsRequired githubv4.Boolean `graphql:"isRequired(pullRequestNumber: $number)"`
+	CheckSuite struct {
+		WorkflowRun *WorkflowRun
 	}
-	if err != nil {
-		return false, errors.Wrap(err, "getting required status checks")
+}
+
+func (original CheckRun) Copy() CheckRun {
+	copy := CheckRun{
+		Name:       original.Name,
+		Conclusion: original.Conclusion,
+		IsRequired: original.IsRequired,
+		CheckSuite: original.CheckSuite,
 	}
+	if original.CheckSuite.WorkflowRun != nil {
+		copy.CheckSuite.WorkflowRun = new(WorkflowRun)
+		*copy.CheckSuite.WorkflowRun = *original.CheckSuite.WorkflowRun
+	}
+	return copy
+}
 
-	if required.RequiredStatusChecks == nil {
-		return true, nil
+type StatusContext struct {
+	Context githubv4.String
+	State   githubv4.String
+	// Not currently used: GitHub API classifies as required if coming from ruleset, even when the ruleset is not enforced!
+	IsRequired githubv4.Boolean `graphql:"isRequired(pullRequestNumber: $number)"`
+}
+
+func (g *GithubClient) LookupRepoId(repo githubv4.String) (githubv4.Int, error) {
+	// This function may get many calls for the same repo, and repo names are not often changed
+	// Utilize caching to reduce the number of API calls to GitHub
+	if repoId, ok := g.repoIdCache.Get(repo); ok {
+		return repoId, nil
 	}
 
-	//check check suite/check run api
-	checksuites, resp, err := g.client.Checks.ListCheckSuitesForRef(context.Background(), *pull.Head.Repo.Owner.Login, repo.Name, *pull.Head.Ref, nil)
-	if resp != nil {
-		logger.Debug("GET /repos/%v/%v/commits/%s/check-suites returned: %v", *pull.Head.Repo.Owner.Login, repo.Name, *pull.Head.Ref, resp.StatusCode)
+	repoSplit := strings.Split(string(repo), "/")
+	if len(repoSplit) != 2 {
+		return githubv4.Int(0), fmt.Errorf("invalid repository name: %s", repo)
 	}
+
+	var query struct {
+		Repository struct {
+			DatabaseId githubv4.Int
+		} `graphql:"repository(owner: $owner, name: $name)"`
+	}
+	variables := map[string]interface{}{
+		"owner": githubv4.String(repoSplit[0]),
+		"name":  githubv4.String(repoSplit[1]),
+	}
+
+	err := g.v4Client.Query(g.ctx, &query, variables)
+
 	if err != nil {
-		return false, errors.Wrap(err, "getting check suites for ref")
+		return githubv4.Int(0), errors.Wrap(err, "getting repository id from GraphQL")
 	}
 
-	//iterate over check completed check suites - return false if we find one that doesnt have conclusion = "success"
-	for _, c := range checksuites.CheckSuites {
-		if *c.Status == "completed" {
-			//iterate over the runs inside the suite
-			suite, resp, err := g.client.Checks.ListCheckRunsCheckSuite(context.Background(), *pull.Head.Repo.Owner.Login, repo.Name, *c.ID, nil)
-			if resp != nil {
-				logger.Debug("GET /repos/%v/%v/check-suites/%d/check-runs returned: %v", *pull.Head.Repo.Owner.Login, repo.Name, *c.ID, resp.StatusCode)
-			}
-			if err != nil {
-				return false, errors.Wrap(err, "getting check runs for check suite")
-			}
+	g.repoIdCache.Set(repo, query.Repository.DatabaseId)
 
-			for _, r := range suite.CheckRuns {
-				//check to see if the check is required
-				if isRequiredCheck(*r.Name, required.RequiredStatusChecks.Contexts) {
-					if *c.Conclusion == "success" {
-						continue
-					}
-					return false, nil
-				}
-				//ignore checks that arent required
-				continue
-			}
-		}
+	return query.Repository.DatabaseId, nil
+}
+
+func (g *GithubClient) WorkflowRunMatchesWorkflowFileReference(workflowRun WorkflowRun, workflowFileReference WorkflowFileReference) (bool, error) {
+	// Unfortunately, the GitHub API doesn't expose the repositoryId for the WorkflowRunFile from the statusCheckRollup.
+	// Conversely, it doesn't expose the repository name for the WorkflowFileReference from the RepositoryRuleConnection.
+	// Therefore, a second query is required to lookup the association between repositoryId and repositoryName.
+	repoId, err := g.LookupRepoId(workflowRun.File.RepositoryName)
+	if err != nil {
+		return false, err
 	}
 
-	return true, nil
+	if !(repoId == workflowFileReference.RepositoryId && workflowRun.File.Path == workflowFileReference.Path) {
+		return false, nil
+	} else if workflowFileReference.Sha != nil {
+		return strings.Contains(string(workflowRun.File.RepositoryFileUrl), string(*workflowFileReference.Sha)), nil
+	} else {
+		return true, nil
+	}
 }
 
-// GetPullReviewDecision gets the pull review decision, which takes into account CODEOWNERS
-func (g *GithubClient) GetPullReviewDecision(repo models.Repo, pull models.PullRequest) (approvalStatus bool, err error) {
+func (g *GithubClient) GetPullRequestMergeabilityInfo(
+	repo models.Repo,
+	pull *github.PullRequest,
+) (
+	reviewDecision githubv4.String,
+	requiredChecks []githubv4.String,
+	requiredWorkflows []WorkflowFileReference,
+	checkRuns []CheckRun,
+	statusContexts []StatusContext,
+	err error,
+) {
 	var query struct {
 		Repository struct {
 			PullRequest struct {
-				ReviewDecision string
+				ReviewDecision githubv4.String
+				BaseRef        struct {
+					BranchProtectionRule struct {
+						RequiredStatusChecks []struct {
+							Context githubv4.String
+						}
+					}
+					Rules struct {
+						PageInfo PageInfo
+						Nodes    []struct {
+							Type              githubv4.String
+							RepositoryRuleset struct {
+								Enforcement githubv4.String
+							}
+							Parameters struct {
+								RequiredStatusChecksParameters struct {
+									RequiredStatusChecks []struct {
+										Context githubv4.String
+									}
+								} `graphql:"... on RequiredStatusChecksParameters"`
+								WorkflowsParameters struct {
+									Workflows []WorkflowFileReference
+								} `graphql:"... on WorkflowsParameters"`
+							}
+						}
+					} `graphql:"rules(first: 100, after: $ruleCursor)"`
+				}
+				Commits struct {
+					Nodes []struct {
+						Commit struct {
+							StatusCheckRollup struct {
+								Contexts struct {
+									PageInfo PageInfo
+									Nodes    []struct {
+										Typename      githubv4.String `graphql:"__typename"`
+										CheckRun      CheckRun        `graphql:"... on CheckRun"`
+										StatusContext StatusContext   `graphql:"... on StatusContext"`
+									}
+								} `graphql:"contexts(first: 100, after: $contextCursor)"`
+							}
+						}
+					}
+				} `graphql:"commits(last: 1)"`
 			} `graphql:"pullRequest(number: $number)"`
 		} `graphql:"repository(owner: $owner, name: $name)"`
 	}
 
 	variables := map[string]interface{}{
-		"owner":  githubv4.String(repo.Owner),
-		"name":   githubv4.String(repo.Name),
-		"number": githubv4.Int(pull.Num),
+		"owner":         githubv4.String(repo.Owner),
+		"name":          githubv4.String(repo.Name),
+		"number":        githubv4.Int(*pull.Number),
+		"ruleCursor":    (*githubv4.String)(nil),
+		"contextCursor": (*githubv4.String)(nil),
+	}
+
+	requiredChecksSet := make(map[githubv4.String]any)
+
+pagination:
+	for {
+		err = g.v4Client.Query(g.ctx, &query, variables)
+
+		if err != nil {
+			break pagination
+		}
+
+		reviewDecision = query.Repository.PullRequest.ReviewDecision
+
+		for _, rule := range query.Repository.PullRequest.BaseRef.BranchProtectionRule.RequiredStatusChecks {
+			requiredChecksSet[rule.Context] = struct{}{}
+		}
+
+		for _, rule := range query.Repository.PullRequest.BaseRef.Rules.Nodes {
+			if rule.RepositoryRuleset.Enforcement != "ACTIVE" {
+				continue
+			}
+			switch rule.Type {
+			case "REQUIRED_STATUS_CHECKS":
+				for _, context := range rule.Parameters.RequiredStatusChecksParameters.RequiredStatusChecks {
+					requiredChecksSet[context.Context] = struct{}{}
+				}
+			case "WORKFLOWS":
+				for _, workflow := range rule.Parameters.WorkflowsParameters.Workflows {
+					requiredWorkflows = append(requiredWorkflows, workflow.Copy())
+				}
+			default:
+				continue
+			}
+		}
+
+		if len(query.Repository.PullRequest.Commits.Nodes) == 0 {
+			err = errors.New("no commits found on PR")
+			break pagination
+		}
+
+		for _, context := range query.Repository.PullRequest.Commits.Nodes[0].Commit.StatusCheckRollup.Contexts.Nodes {
+			switch context.Typename {
+			case "CheckRun":
+				checkRuns = append(checkRuns, context.CheckRun.Copy())
+			case "StatusContext":
+				statusContexts = append(statusContexts, context.StatusContext)
+			default:
+				err = fmt.Errorf("unknown type of status check, %q", context.Typename)
+				break pagination
+			}
+		}
+
+		if !query.Repository.PullRequest.BaseRef.Rules.PageInfo.HasNextPage &&
+			!query.Repository.PullRequest.Commits.Nodes[0].Commit.StatusCheckRollup.Contexts.PageInfo.HasNextPage {
+			break pagination
+		}
+
+		if query.Repository.PullRequest.BaseRef.Rules.PageInfo.EndCursor != nil {
+			variables["ruleCursor"] = query.Repository.PullRequest.BaseRef.Rules.PageInfo.EndCursor
+		}
+		if query.Repository.PullRequest.Commits.Nodes[0].Commit.StatusCheckRollup.Contexts.PageInfo.EndCursor != nil {
+			variables["contextCursor"] = query.Repository.PullRequest.Commits.Nodes[0].Commit.StatusCheckRollup.Contexts.PageInfo.EndCursor
+		}
 	}
 
-	err = g.v4Client.Query(g.ctx, &query, variables)
 	if err != nil {
-		return approvalStatus, errors.Wrap(err, "getting reviewDecision")
+		return "", nil, nil, nil, nil, errors.Wrap(err, "fetching rulesets, branch protections and status checks from GraphQL")
 	}
 
-	if query.Repository.PullRequest.ReviewDecision == "APPROVED" || len(query.Repository.PullRequest.ReviewDecision) == 0 {
-		return true, nil
+	for context := range requiredChecksSet {
+		requiredChecks = append(requiredChecks, context)
+	}
+
+	return reviewDecision, requiredChecks, requiredWorkflows, checkRuns, statusContexts, nil
+}
+
+func CheckRunPassed(checkRun CheckRun) bool {
+	return checkRun.Conclusion == "SUCCESS" || checkRun.Conclusion == "SKIPPED" || checkRun.Conclusion == "NEUTRAL"
+}
+
+func StatusContextPassed(statusContext StatusContext, vcsstatusname string) bool {
+	return strings.HasPrefix(string(statusContext.Context), fmt.Sprintf("%s/%s", vcsstatusname, command.Apply.String())) ||
+		statusContext.State == "SUCCESS"
+}
+
+func ExpectedCheckPassed(expectedContext githubv4.String, checkRuns []CheckRun, statusContexts []StatusContext, vcsstatusname string) bool {
+	for _, checkRun := range checkRuns {
+		if checkRun.Name == expectedContext {
+			return CheckRunPassed(checkRun)
+		}
+	}
+
+	for _, statusContext := range statusContexts {
+		if statusContext.Context == expectedContext {
+			return StatusContextPassed(statusContext, vcsstatusname)
+		}
+	}
+
+	return false
+}
+
+func (g *GithubClient) ExpectedWorkflowPassed(expectedWorkflow WorkflowFileReference, checkRuns []CheckRun) (bool, error) {
+	for _, checkRun := range checkRuns {
+		if checkRun.CheckSuite.WorkflowRun == nil {
+			continue
+		}
+		match, err := g.WorkflowRunMatchesWorkflowFileReference(*checkRun.CheckSuite.WorkflowRun, expectedWorkflow)
+		if err != nil {
+			return false, err
+		}
+		if match {
+			return CheckRunPassed(checkRun), nil
+		}
 	}
 
 	return false, nil
 }
 
+// IsMergeableMinusApply checks review decision (which takes into account CODEOWNERS) and required checks for PR (excluding the atlantis apply check).
+func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo models.Repo, pull *github.PullRequest, vcsstatusname string) (bool, error) {
+	if pull.Number == nil {
+		return false, errors.New("pull request number is nil")
+	}
+	reviewDecision, requiredChecks, requiredWorkflows, checkRuns, statusContexts, err := g.GetPullRequestMergeabilityInfo(repo, pull)
+	if err != nil {
+		return false, err
+	}
+
+	notMergeablePrefix := fmt.Sprintf("Pull Request %s/%s:%s is not mergeable", repo.Owner, repo.Name, strconv.Itoa(*pull.Number))
+
+	// Review decision takes CODEOWNERS into account
+	// Empty review decision means review is not required
+	if reviewDecision != "APPROVED" && len(reviewDecision) != 0 {
+		logger.Debug("%s: Review Decision: %s", notMergeablePrefix, reviewDecision)
+		return false, nil
+	}
+
+	// The statusCheckRollup does not always contain all required checks
+	// For example, if a check was made required after the pull request was opened, it would be missing
+	// Go through all checks and workflows required by branch protection or rulesets
+	// Make sure that they can all be found in the statusCheckRollup and that they all pass
+	for _, requiredCheck := range requiredChecks {
+		if !ExpectedCheckPassed(requiredCheck, checkRuns, statusContexts, vcsstatusname) {
+			logger.Debug("%s: Expected Required Check: %s", notMergeablePrefix, requiredCheck)
+			return false, nil
+		}
+	}
+	for _, requiredWorkflow := range requiredWorkflows {
+		passed, err := g.ExpectedWorkflowPassed(requiredWorkflow, checkRuns)
+		if err != nil {
+			return false, err
+		}
+		if !passed {
+			logger.Debug("%s: Expected Required Workflow: RepositoryId: %d Path: %s", notMergeablePrefix, requiredWorkflow.RepositoryId, requiredWorkflow.Path)
+			return false, nil
+		}
+	}
+
+	return true, nil
+}
+
 // PullIsMergeable returns true if the pull request is mergeable.
 func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) {
 	logger.Debug("Checking if GitHub pull request %d is mergeable", pull.Num)
@@ -531,7 +798,6 @@ func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models
 		return false, errors.Wrap(err, "getting pull request")
 	}
 
-	state := githubPR.GetMergeableState()
 	// We map our mergeable check to when the GitHub merge button is clickable.
 	// This corresponds to the following states:
 	// clean: No conflicts, all requirements satisfied.
@@ -541,33 +807,22 @@ func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models
 	// has_hooks: GitHub Enterprise only, if a repo has custom pre-receive
 	//            hooks. Merging is allowed (green box).
 	// See: https://github.com/octokit/octokit.net/issues/1763
-	if state != "clean" && state != "unstable" && state != "has_hooks" {
-		//mergeable bypass apply code hidden by feature flag
+	switch githubPR.GetMergeableState() {
+	case "clean", "unstable", "has_hooks":
+		return true, nil
+	case "blocked":
 		if g.config.AllowMergeableBypassApply {
 			logger.Debug("AllowMergeableBypassApply feature flag is enabled - attempting to bypass apply from mergeable requirements")
-			if state == "blocked" {
-				//check status excluding atlantis apply
-				status, err := g.GetCombinedStatusMinusApply(logger, repo, githubPR, vcsstatusname)
-				if err != nil {
-					return false, errors.Wrap(err, "getting pull request status")
-				}
-
-				//check to see if pr is approved using reviewDecision
-				approved, err := g.GetPullReviewDecision(repo, pull)
-				if err != nil {
-					return false, errors.Wrap(err, "getting pull request reviewDecision")
-				}
-
-				//if all other status checks EXCEPT atlantis/apply are successful, and the PR is approved based on reviewDecision, let it proceed
-				if status && approved {
-					return true, nil
-				}
+			isMergeableMinusApply, err := g.IsMergeableMinusApply(logger, repo, githubPR, vcsstatusname)
+			if err != nil {
+				return false, errors.Wrap(err, "getting pull request status")
 			}
+			return isMergeableMinusApply, nil
 		}
-
+		return false, nil
+	default:
 		return false, nil
 	}
-	return true, nil
 }
 
 // GetPullRequest returns the pull request.


Test patch:
diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go
index 44ad33a1c4..ef97c64245 100644
--- a/server/events/vcs/github_client_test.go
+++ b/server/events/vcs/github_client_test.go
@@ -569,22 +569,6 @@ func TestGithubClient_PullIsMergeable(t *testing.T) {
 	Ok(t, err)
 	prJSON := string(jsBytes)
 
-	// Status Check Response
-	jsBytes, err = os.ReadFile("testdata/github-commit-status-full.json")
-	Ok(t, err)
-	commitJSON := string(jsBytes)
-
-	//reviewDecision Response
-	reviewDecision := `{
-		"data": {
-			"repository": {
-				"pullRequest": {
-					"reviewDecision": "REVIEW_REQUIRED"
-				}
-			}
-		}
-	}`
-
 	for _, c := range cases {
 		t.Run(c.state, func(t *testing.T) {
 			response := strings.Replace(prJSON,
@@ -599,13 +583,6 @@ func TestGithubClient_PullIsMergeable(t *testing.T) {
 					case "/api/v3/repos/owner/repo/pulls/1":
 						w.Write([]byte(response)) // nolint: errcheck
 						return
-					case "/api/v3/repos/owner/repo/pulls/1/reviews?per_page=300":
-						w.Write([]byte("[]")) // nolint: errcheck
-						return
-					case "/api/v3/repos/owner/repo/commits/new-topic/status":
-						w.Write([]byte(commitJSON)) // nolint: errcheck
-					case "/api/graphql":
-						w.Write([]byte(reviewDecision)) // nolint: errcheck
 					default:
 						t.Errorf("got unexpected request at %q", r.RequestURI)
 						http.Error(w, "not found", http.StatusNotFound)
@@ -643,167 +620,177 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T)
 	logger := logging.NewNoopLogger(t)
 	vcsStatusName := "atlantis"
 	cases := []struct {
-		state          string
-		reviewDecision string
-		expMergeable   bool
+		state                     string
+		statusCheckRollupFilePath string
+		reviewDecision            string
+		expMergeable              bool
 	}{
 		{
 			"dirty",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			false,
 		},
 		{
 			"unknown",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			false,
 		},
 		{
 			"blocked",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			false,
 		},
 		{
 			"blocked",
+			"ruleset-atlantis-apply-pending.json",
 			`"APPROVED"`,
 			true,
 		},
 		{
 			"blocked",
+			"ruleset-atlantis-apply-pending.json",
 			"null",
 			true,
 		},
 		{
 			"behind",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			false,
 		},
 		{
 			"random",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			false,
 		},
 		{
 			"unstable",
+			"ruleset-atlantis-apply-pending.json",
 			`"REVIEW_REQUIRED"`,
 			true,
 		},
 		{
 			"has_hooks",
+			"ruleset-atlantis-apply-pending.json",
 			`"APPROVED"`,
 			true,
 		},
 		{
 			"clean",
+			"ruleset-atlantis-apply-pending.json",
 			`"APPROVED"`,
 			true,
 		},
 		{
 			"",
+			"ruleset-atlantis-apply-pending.json",
 			`"APPROVED"`,
 			false,
 		},
-	}
-
-	// Use a real GitHub json response and edit the mergeable_state field.
-	jsBytes, err := os.ReadFile("testdata/github-pull-request.json")
-	Ok(t, err)
-	prJSON := string(jsBytes)
-
-	// Status Check Response
-	jsBytes, err = os.ReadFile("testdata/github-commit-status-full.json")
-	Ok(t, err)
-	commitJSON := string(jsBytes)
-
-	// Branch protection Response
-	jsBytes, err = os.ReadFile("testdata/github-branch-protection-required-checks.json")
-	Ok(t, err)
-	branchProtectionJSON := string(jsBytes)
-
-	// List check suites Response
-	jsBytes, err = os.ReadFile("testdata/github-commit-check-suites.json")
-	Ok(t, err)
-	checkSuites := string(jsBytes)
-
-	for _, c := range cases {
-		t.Run(c.state, func(t *testing.T) {
-			response := strings.Replace(prJSON,
-				`"mergeable_state": "clean"`,
-				fmt.Sprintf(`"mergeable_state": "%s"`, c.state),
-				1,
-			)
-
-			// reviewDecision Response
-			reviewDecision := fmt.Sprintf(`{
-				"data": {
-					"repository": {
-						"pullRequest": {
-							"reviewDecision": %s
-						}
-					}
-				}
-			}`, c.reviewDecision)
-
-			testServer := httptest.NewTLSServer(
-				http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-					switch r.RequestURI {
-					case "/api/v3/repos/octocat/repo/pulls/1":
-						w.Write([]byte(response)) // nolint: errcheck
-						return
-					case "/api/v3/repos/octocat/repo/pulls/1/reviews?per_page=300":
-						w.Write([]byte("[]")) // nolint: errcheck
-						return
-					case "/api/v3/repos/octocat/repo/commits/new-topic/status":
-						w.Write([]byte(commitJSON)) // nolint: errcheck
-					case "/api/graphql":
-						w.Write([]byte(reviewDecision)) // nolint: errcheck
-					case "/api/v3/repos/octocat/repo/branches/main/protection":
-						w.Write([]byte(branchProtectionJSON)) // nolint: errcheck
-					case "/api/v3/repos/octocat/repo/commits/new-topic/check-suites":
-						w.Write([]byte(checkSuites)) // nolint: errcheck
-					default:
-						t.Errorf("got unexpected request at %q", r.RequestURI)
-						http.Error(w, "not found", http.StatusNotFound)
-						return
-					}
-				}))
-			testServerURL, err := url.Parse(testServer.URL)
-			Ok(t, err)
-			client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{AllowMergeableBypassApply: true}, 0, logging.NewNoopLogger(t))
-			Ok(t, err)
-			defer disableSSLVerification()()
-
-			actMergeable, err := client.PullIsMergeable(
-				logger,
-				models.Repo{
-					FullName:          "octocat/repo",
-					Owner:             "octocat",
-					Name:              "repo",
-					CloneURL:          "",
-					SanitizedCloneURL: "",
-					VCSHost: models.VCSHost{
-						Type:     models.Github,
-						Hostname: "github.com",
-					},
-				}, models.PullRequest{
-					Num: 1,
-				}, vcsStatusName)
-			Ok(t, err)
-			Equals(t, c.expMergeable, actMergeable)
-		})
-	}
-}
-
-func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApplyButWithNoBranchProtectionChecks(t *testing.T) {
-	logger := logging.NewNoopLogger(t)
-	vcsStatusName := "atlantis"
-	cases := []struct {
-		state          string
-		reviewDecision string
-		expMergeable   bool
-	}{
 		{
 			"blocked",
-			`"REVIEW_REQUIRED"`,
+			"ruleset-optional-check-failed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-optional-status-failed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-check-pending.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"ruleset-check-skipped.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-check-neutral.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-evaluate-workflow-failed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"branch-protection-expected.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"branch-protection-failed.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"branch-protection-passed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-check-expected.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"ruleset-check-failed.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"ruleset-check-passed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-workflow-expected.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"ruleset-workflow-failed.json",
+			`"APPROVED"`,
+			false,
+		},
+		{
+			"blocked",
+			"ruleset-workflow-passed.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-workflow-passed-sha-match.json",
+			`"APPROVED"`,
+			true,
+		},
+		{
+			"blocked",
+			"ruleset-workflow-passed-sha-mismatch.json",
+			`"APPROVED"`,
 			false,
 		},
 	}
@@ -813,25 +800,9 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApplyButWithNoBranc
 	Ok(t, err)
 	prJSON := string(jsBytes)
 
-	// Status Check Response
-	jsBytes, err = os.ReadFile("testdata/github-commit-status-full.json")
-	Ok(t, err)
-	commitJSON := string(jsBytes)
-
-	// Branch protection Response
-	jsBytes, err = os.ReadFile("testdata/github-branch-protection-no-required-checks.json")
+	jsBytes, err = os.ReadFile("testdata/github-pull-request-mergeability/repository-id.json")
 	Ok(t, err)
-	branchProtectionJSON := string(jsBytes)
-
-	// List check suites Response
-	jsBytes, err = os.ReadFile("testdata/github-commit-check-suites-completed.json")
-	Ok(t, err)
-	checkSuites := string(jsBytes)
-
-	// List check runs in a check suite
-	jsBytes, err = os.ReadFile("testdata/github-commit-check-suites-check-runs-completed.json")
-	Ok(t, err)
-	checkRuns := string(jsBytes)
+	repoIdJSON := string(jsBytes)
 
 	for _, c := range cases {
 		t.Run(c.state, func(t *testing.T) {
@@ -841,36 +812,41 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApplyButWithNoBranc
 				1,
 			)
 
-			// reviewDecision Response
-			reviewDecision := fmt.Sprintf(`{
-				"data": {
-					"repository": {
-						"pullRequest": {
-							"reviewDecision": %s
-						}
-					}
-				}
-			}`, c.reviewDecision)
+			// PR review decision and checks statuses Response
+			jsBytes, err = os.ReadFile("testdata/github-pull-request-mergeability/" + c.statusCheckRollupFilePath)
+			Ok(t, err)
+			prMergeableStatusJSON := string(jsBytes)
+
+			// PR review decision and checks statuses Response
+			prMergeableStatus := strings.Replace(prMergeableStatusJSON,
+				`"reviewDecision": null,`,
+				fmt.Sprintf(`"reviewDecision": %s,`, c.reviewDecision),
+				1,
+			)
 
 			testServer := httptest.NewTLSServer(
 				http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 					switch r.RequestURI {
-					case "/api/v3/repos/octocat/Hello-World/pulls/1":
+					case "/api/v3/repos/octocat/repo/pulls/1":
 						w.Write([]byte(response)) // nolint: errcheck
 						return
-					case "/api/v3/repos/octocat/Hello-World/pulls/1/reviews?per_page=300":
-						w.Write([]byte("[]")) // nolint: errcheck
-						return
-					case "/api/v3/repos/octocat/Hello-World/commits/new-topic/status":
-						w.Write([]byte(commitJSON)) // nolint: errcheck
 					case "/api/graphql":
-						w.Write([]byte(reviewDecision)) // nolint: errcheck
-					case "/api/v3/repos/octocat/Hello-World/branches/main/protection":
-						w.Write([]byte(branchProtectionJSON)) // nolint: errcheck
-					case "/api/v3/repos/octocat/Hello-World/commits/new-topic/check-suites":
-						w.Write([]byte(checkSuites)) // nolint: errcheck
-					case "/api/v3/repos/octocat/Hello-World/check-suites/1234567890/check-runs":
-						w.Write([]byte(checkRuns)) // nolint: errcheck
+						body, err := io.ReadAll(r.Body)
+						if err != nil {
+							t.Errorf("read body error: %v", err)
+							http.Error(w, "", http.StatusInternalServerError)
+							return
+						}
+						if strings.Contains(string(body), "pullRequest(") {
+							w.Write([]byte(prMergeableStatus)) // nolint: errcheck
+							return
+						} else if strings.Contains(string(body), "databaseId") {
+							w.Write([]byte(repoIdJSON)) // nolint: errcheck
+							return
+						}
+						t.Errorf("got unexpected request at %q", r.RequestURI)
+						http.Error(w, "not found", http.StatusNotFound)
+						return
 					default:
 						t.Errorf("got unexpected request at %q", r.RequestURI)
 						http.Error(w, "not found", http.StatusNotFound)
@@ -886,9 +862,9 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApplyButWithNoBranc
 			actMergeable, err := client.PullIsMergeable(
 				logger,
 				models.Repo{
-					FullName:          "octocat/Hello-World",
+					FullName:          "octocat/repo",
 					Owner:             "octocat",
-					Name:              "Hello-World",
+					Name:              "repo",
 					CloneURL:          "",
 					SanitizedCloneURL: "",
 					VCSHost: models.VCSHost{
diff --git a/server/events/vcs/testdata/github-branch-protection-no-required-checks.json b/server/events/vcs/testdata/github-branch-protection-no-required-checks.json
deleted file mode 100644
index 4dd1496b79..0000000000
--- a/server/events/vcs/testdata/github-branch-protection-no-required-checks.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    "url": "https://api.github.com/repos/octocat/repo/branches/master/protection",
-    "required_pull_request_reviews": {
-        "url": "https://api.github.com/repos/octocat/repo/branches/master/protection/required_pull_request_reviews",
-        "dismiss_stale_reviews": false,
-        "require_code_owner_reviews": false,
-        "require_last_push_approval": false,
-        "required_approving_review_count": 1
-    }
-}
diff --git a/server/events/vcs/testdata/github-branch-protection-required-checks.json b/server/events/vcs/testdata/github-branch-protection-required-checks.json
deleted file mode 100644
index 9f422db9ea..0000000000
--- a/server/events/vcs/testdata/github-branch-protection-required-checks.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "url": "https://api.github.com/repos/octocat/Hello-World/branches/master/protection",
-    "required_status_checks": {
-        "url": "https://api.github.com/repos/octocat/Hello-World/branches/master/protection/required_status_checks",
-        "strict": true,
-        "contexts": [
-            "atlantis/apply"
-        ],
-        "contexts_url": "https://api.github.com/repos/octocat/Hello-World/branches/master/protection/required_status_checks/contexts",
-        "checks": [
-            {
-                "context": "atlantis/apply",
-                "app_id": 123456
-            }
-        ]
-    }
-}
diff --git a/server/events/vcs/testdata/github-commit-check-suites-check-runs-completed.json b/server/events/vcs/testdata/github-commit-check-suites-check-runs-completed.json
deleted file mode 100644
index 125e4d1ddf..0000000000
--- a/server/events/vcs/testdata/github-commit-check-suites-check-runs-completed.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
-    "total_count": 1,
-    "check_runs": [
-        {
-            "id": 4,
-            "head_sha": "ce587453ced02b1526dfb4cb910479d431683101",
-            "node_id": "MDg6Q2hlY2tSdW40",
-            "external_id": "",
-            "url": "https://api.github.com/repos/github/hello-world/check-runs/4",
-            "html_url": "https://github.com/github/hello-world/runs/4",
-            "details_url": "https://example.com",
-            "status": "completed",
-            "conclusion": "success",
-            "started_at": "2018-05-04T01:14:52Z",
-            "completed_at": "2018-05-04T01:14:52Z",
-            "output": {
-                "title": "Mighty Readme report",
-                "summary": "There are 0 failures, 2 warnings, and 1 notice.",
-                "text": "You may have some misspelled words on lines 2 and 4. You also may want to add a section in your README about how to install your app.",
-                "annotations_count": 2,
-                "annotations_url": "https://api.github.com/repos/github/hello-world/check-runs/4/annotations"
-            },
-            "name": "mighty_readme",
-            "check_suite": {
-                "id": 5
-            },
-            "app": {
-                "id": 1,
-                "slug": "octoapp",
-                "node_id": "MDExOkludGVncmF0aW9uMQ==",
-                "owner": {
-                    "login": "github",
-                    "id": 1,
-                    "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=",
-                    "url": "https://api.github.com/orgs/github",
-                    "repos_url": "https://api.github.com/orgs/github/repos",
-                    "events_url": "https://api.github.com/orgs/github/events",
-                    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
-                    "gravatar_id": "",
-                    "html_url": "https://github.com/octocat",
-                    "followers_url": "https://api.github.com/users/octocat/followers",
-                    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-                    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-                    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-                    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-                    "organizations_url": "https://api.github.com/users/octocat/orgs",
-                    "received_events_url": "https://api.github.com/users/octocat/received_events",
-                    "type": "User",
-                    "site_admin": true
-                },
-                "name": "Octocat App",
-                "description": "",
-                "external_url": "https://example.com",
-                "html_url": "https://github.com/apps/octoapp",
-                "created_at": "2017-07-08T16:18:44-04:00",
-                "updated_at": "2017-07-08T16:18:44-04:00",
-                "permissions": {
-                    "metadata": "read",
-                    "contents": "read",
-                    "issues": "write",
-                    "single_file": "write"
-                },
-                "events": [
-                    "push",
-                    "pull_request"
-                ]
-            },
-            "pull_requests": [
-                {
-                    "url": "https://api.github.com/repos/github/hello-world/pulls/1",
-                    "id": 1934,
-                    "number": 3956,
-                    "head": {
-                        "ref": "say-hello",
-                        "sha": "3dca65fa3e8d4b3da3f3d056c59aee1c50f41390",
-                        "repo": {
-                            "id": 526,
-                            "url": "https://api.github.com/repos/github/hello-world",
-                            "name": "hello-world"
-                        }
-                    },
-                    "base": {
-                        "ref": "master",
-                        "sha": "e7fdf7640066d71ad16a86fbcbb9c6a10a18af4f",
-                        "repo": {
-                            "id": 526,
-                            "url": "https://api.github.com/repos/github/hello-world",
-                            "name": "hello-world"
-                        }
-                    }
-                }
-            ]
-        }
-    ]
-}
diff --git a/server/events/vcs/testdata/github-commit-check-suites-completed.json b/server/events/vcs/testdata/github-commit-check-suites-completed.json
deleted file mode 100644
index b8af9c32a9..0000000000
--- a/server/events/vcs/testdata/github-commit-check-suites-completed.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
-    "total_count": 1,
-    "check_suites": [
-        {
-            "id": 1234567890,
-            "node_id": "CS_kwDOHE7PYM8AAAAB2iIZfQ",
-            "head_branch": "atlantis-patch-2",
-            "head_sha": "4273e07c528292222f119a040079093bf1f11232",
-            "status": "completed",
-            "conclusion": null,
-            "url": "https://api.github.com/repos/octocat/Hello-World/check-suites/1234567890",
-            "before": "0000000000000000000000000000000000000000",
-            "after": "4273e07c528292222f119a040079093bf1f11232",
-            "pull_requests": [
-                {
-                    "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1",
-                    "id": 1035065545,
-                    "number": 1,
-                    "head": {
-                        "ref": "atlantis-patch-2",
-                        "sha": "4273e07c528292222f119a040079093bf1f11232",
-                        "repo": {
-                            "id": 474926944,
-                            "url": "https://api.github.com/repos/octocat/Hello-World",
-                            "name": "Hello-World"
-                        }
-                    },
-                    "base": {
-                        "ref": "main",
-                        "sha": "6f5744874b33ceb6a5c91edc91085991dbd1f61a",
-                        "repo": {
-                            "id": 474926944,
-                            "url": "https://api.github.com/repos/octocat/Hello-World",
-                            "name": "Hello-World"
-                        }
-                    }
-                }
-            ],
-            "app": {
-                "id": 184783,
-                "slug": "atlantis",
-                "node_id": "A_kwHOBbMkBs4AAtHP",
-                "owner": {
-                    "login": "octocat",
-                    "id": 95626246,
-                    "node_id": "O_kgDOBbMkBg",
-                    "avatar_url": "https://avatars.githubusercontent.com/u/95626246?v=4",
-                    "gravatar_id": "",
-                    "url": "https://api.github.com/users/octocat",
-                    "html_url": "https://github.com/octocat",
-                    "followers_url": "https://api.github.com/users/octocat/followers",
-                    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-                    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-                    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-                    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-                    "organizations_url": "https://api.github.com/users/octocat/orgs",
-                    "repos_url": "https://api.github.com/users/octocat/repos",
-                    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
-                    "received_events_url": "https://api.github.com/users/octocat/received_events",
-                    "type": "Organization",
-                    "site_admin": false
-                },
-                "name": "atlantis",
-                "description": "",
-                "external_url": "https://atlantis.localhost/",
-                "html_url": "https://github.com/apps/atlantis",
-                "created_at": "2022-03-29T08:51:26Z",
-                "updated_at": "2022-07-01T11:35:37Z",
-                "permissions": {
-                    "administration": "read",
-                    "checks": "write",
-                    "contents": "write",
-                    "issues": "write",
-                    "metadata": "read",
-                    "pull_requests": "write",
-                    "statuses": "write"
-                },
-                "events": []
-            },
-            "created_at": "2022-08-24T07:06:21Z",
-            "updated_at": "2022-08-24T07:06:21Z",
-            "rerequestable": true,
-            "runs_rerequestable": true,
-            "latest_check_runs_count": 0,
-            "check_runs_url": "https://api.github.com/repos/octocat/Hello-World/check-suites/1234567890/check-runs",
-            "head_commit": {
-                "id": "4273e07c528292222f119a040079093bf1f11232",
-                "tree_id": "56781332464aabdfae51b7f37f72ffc6ce8ce54e",
-                "message": "test atlantis",
-                "timestamp": "2022-08-24T07:06:21Z",
-                "author": {
-                    "name": "octocat",
-                    "email": "octocat@noreply.github.com"
-                },
-                "committer": {
-                    "name": "GitHub",
-                    "email": "noreply@github.com"
-                }
-            },
-            "repository": {
-                "id": 474926944,
-                "node_id": "R_kgDOHE7PYA",
-                "name": "Hello-World",
-                "full_name": "octocat/Hello-World",
-                "private": true,
-                "owner": {
-                    "login": "octocat",
-                    "id": 95626246,
-                    "node_id": "O_kgDOBbMkBg",
-                    "avatar_url": "https://avatars.githubusercontent.com/u/95626246?v=4",
-                    "gravatar_id": "",
-                    "url": "https://api.github.com/users/octocat",
-                    "html_url": "https://github.com/octocat",
-                    "followers_url": "https://api.github.com/users/octocat/followers",
-                    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-                    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-                    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-                    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-                    "organizations_url": "https://api.github.com/users/octocat/orgs",
-                    "repos_url": "https://api.github.com/users/octocat/repos",
-                    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
-                    "received_events_url": "https://api.github.com/users/octocat/received_events",
-                    "type": "Organization",
-                    "site_admin": false
-                },
-                "html_url": "https://github.com/octocat/Hello-World",
-                "description": null,
-                "fork": false,
-                "url": "https://api.github.com/repos/octocat/Hello-World",
-                "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
-                "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
-                "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
-                "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
-                "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
-                "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
-                "events_url": "https://api.github.com/repos/octocat/Hello-World/events",
-                "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
-                "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
-                "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
-                "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
-                "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
-                "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
-                "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
-                "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
-                "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
-                "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
-                "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
-                "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
-                "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
-                "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
-                "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
-                "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
-                "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
-                "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
-                "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
-                "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
-                "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
-                "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
-                "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
-                "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
-                "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
-                "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
-                "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
-                "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
-                "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments"
-            }
-        }
-    ]
-}
diff --git a/server/events/vcs/testdata/github-commit-check-suites.json b/server/events/vcs/testdata/github-commit-check-suites.json
deleted file mode 100644
index 017aee7e01..0000000000
--- a/server/events/vcs/testdata/github-commit-check-suites.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
-    "total_count": 1,
-    "check_suites": [
-        {
-            "id": 1234567890,
-            "node_id": "CS_kwDOHE7PYM8AAAAB2iIZfQ",
-            "head_branch": "atlantis-patch-2",
-            "head_sha": "4273e07c528292222f119a040079093bf1f11232",
-            "status": "queued",
-            "conclusion": null,
-            "url": "https://api.github.com/repos/octocat/Hello-World/check-suites/1234567890",
-            "before": "0000000000000000000000000000000000000000",
-            "after": "4273e07c528292222f119a040079093bf1f11232",
-            "pull_requests": [
-                {
-                    "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1",
-                    "id": 1035065545,
-                    "number": 1,
-                    "head": {
-                        "ref": "atlantis-patch-2",
-                        "sha": "4273e07c528292222f119a040079093bf1f11232",
-                        "repo": {
-                            "id": 474926944,
-                            "url": "https://api.github.com/repos/octocat/Hello-World",
-                            "name": "Hello-World"
-                        }
-                    },
-                    "base": {
-                        "ref": "main",
-                        "sha": "6f5744874b33ceb6a5c91edc91085991dbd1f61a",
-                        "repo": {
-                            "id": 474926944,
-                            "url": "https://api.github.com/repos/octocat/Hello-World",
-                            "name": "Hello-World"
-                        }
-                    }
-                }
-            ],
-            "app": {
-                "id": 184783,
-                "slug": "atlantis",
-                "node_id": "A_kwHOBbMkBs4AAtHP",
-                "owner": {
-                    "login": "octocat",
-                    "id": 95626246,
-                    "node_id": "O_kgDOBbMkBg",
-                    "avatar_url": "https://avatars.githubusercontent.com/u/95626246?v=4",
-                    "gravatar_id": "",
-                    "url": "https://api.github.com/users/octocat",
-                    "html_url": "https://github.com/octocat",
-                    "followers_url": "https://api.github.com/users/octocat/followers",
-                    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-                    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-                    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-                    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-                    "organizations_url": "https://api.github.com/users/octocat/orgs",
-                    "repos_url": "https://api.github.com/users/octocat/repos",
-                    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
-                    "received_events_url": "https://api.github.com/users/octocat/received_events",
-                    "type": "Organization",
-                    "site_admin": false
-                },
-                "name": "atlantis",
-                "description": "",
-                "external_url": "https://atlantis.localhost/",
-                "html_url": "https://github.com/apps/atlantis",
-                "created_at": "2022-03-29T08:51:26Z",
-                "updated_at": "2022-07-01T11:35:37Z",
-                "permissions": {
-                    "administration": "read",
-                    "checks": "write",
-                    "contents": "write",
-                    "issues": "write",
-                    "metadata": "read",
-                    "pull_requests": "write",
-                    "statuses": "write"
-                },
-                "events": []
-            },
-            "created_at": "2022-08-24T07:06:21Z",
-            "updated_at": "2022-08-24T07:06:21Z",
-            "rerequestable": true,
-            "runs_rerequestable": true,
-            "latest_check_runs_count": 0,
-            "check_runs_url": "https://api.github.com/repos/octocat/Hello-World/check-suites/1234567890/check-runs",
-            "head_commit": {
-                "id": "4273e07c528292222f119a040079093bf1f11232",
-                "tree_id": "56781332464aabdfae51b7f37f72ffc6ce8ce54e",
-                "message": "test atlantis",
-                "timestamp": "2022-08-24T07:06:21Z",
-                "author": {
-                    "name": "octocat",
-                    "email": "octocat@noreply.github.com"
-                },
-                "committer": {
-                    "name": "GitHub",
-                    "email": "noreply@github.com"
-                }
-            },
-            "repository": {
-                "id": 474926944,
-                "node_id": "R_kgDOHE7PYA",
-                "name": "Hello-World",
-                "full_name": "octocat/Hello-World",
-                "private": true,
-                "owner": {
-                    "login": "octocat",
-                    "id": 95626246,
-                    "node_id": "O_kgDOBbMkBg",
-                    "avatar_url": "https://avatars.githubusercontent.com/u/95626246?v=4",
-                    "gravatar_id": "",
-                    "url": "https://api.github.com/users/octocat",
-                    "html_url": "https://github.com/octocat",
-                    "followers_url": "https://api.github.com/users/octocat/followers",
-                    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-                    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-                    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-                    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-                    "organizations_url": "https://api.github.com/users/octocat/orgs",
-                    "repos_url": "https://api.github.com/users/octocat/repos",
-                    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
-                    "received_events_url": "https://api.github.com/users/octocat/received_events",
-                    "type": "Organization",
-                    "site_admin": false
-                },
-                "html_url": "https://github.com/octocat/Hello-World",
-                "description": null,
-                "fork": false,
-                "url": "https://api.github.com/repos/octocat/Hello-World",
-                "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
-                "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
-                "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
-                "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
-                "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
-                "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
-                "events_url": "https://api.github.com/repos/octocat/Hello-World/events",
-                "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
-                "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
-                "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
-                "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
-                "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
-                "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
-                "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
-                "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
-                "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
-                "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
-                "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
-                "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
-                "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
-                "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
-                "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
-                "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
-                "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
-                "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
-                "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
-                "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
-                "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
-                "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
-                "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
-                "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
-                "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
-                "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
-                "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
-                "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
-                "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments"
-            }
-        }
-    ]
-}
diff --git a/server/events/vcs/testdata/github-commit-status-full.json b/server/events/vcs/testdata/github-commit-status-full.json
deleted file mode 100644
index a042101c40..0000000000
--- a/server/events/vcs/testdata/github-commit-status-full.json
+++ /dev/null
@@ -1,148 +0,0 @@
-{
-    "state": "blocked",
-    "statuses": [
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230299674,
-        "node_id": "SC_kwDOFRFvL88AAAADx2a4Gg",
-        "state": "success",
-        "description": "Plan succeeded.",
-        "target_url": "https://localhost/jobs/octocat/Hello-World/1/project1",
-        "context": "atlantis/plan: project1",
-        "created_at": "2022-02-10T15:26:01Z",
-        "updated_at": "2022-02-10T15:26:01Z"
-      },
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230303174,
-        "node_id": "SC_kwDOFRFvL88AAAADx2bFxg",
-        "state": "success",
-        "description": "Plan succeeded.",
-        "target_url": "https://localhost/jobs/octocat/Hello-World/1/project2",
-        "context": "atlantis/plan: project2",
-        "created_at": "2022-02-10T15:26:12Z",
-        "updated_at": "2022-02-10T15:26:12Z"
-      },
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230303679,
-        "node_id": "SC_kwDOFRFvL88AAAADx2bHvw",
-        "state": "success",
-        "description": "2/2 projects planned successfully.",
-        "target_url": "",
-        "context": "atlantis/plan",
-        "created_at": "2022-02-10T15:26:13Z",
-        "updated_at": "2022-02-10T15:26:13Z"
-      },
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230307923,
-        "node_id": "SC_kwDOFRFvL88AAAADx2bYUw",
-        "state": "failure",
-        "description": "Apply failed.",
-        "target_url": "https://localhost/jobs/octocat/Hello-World/1/project1",
-        "context": "atlantis/apply: project1",
-        "created_at": "2022-02-10T15:26:27Z",
-        "updated_at": "2022-02-10T15:26:27Z"
-      },
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230308153,
-        "node_id": "SC_kwDOFRFvL88AAAADx2bZOQ",
-        "state": "failure",
-        "description": "Apply failed.",
-        "target_url": "https://localhost/jobs/octocat/Hello-World/1/project2",
-        "context": "atlantis/apply: project2",
-        "created_at": "2022-02-10T15:26:27Z",
-        "updated_at": "2022-02-10T15:26:27Z"
-      },
-      {
-        "url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "id": 16230308528,
-        "node_id": "SC_kwDOFRFvL88AAAADx2basA",
-        "state": "failure",
-        "description": "0/2 projects applied successfully.",
-        "target_url": "",
-        "context": "atlantis/apply",
-        "created_at": "2022-02-10T15:26:28Z",
-        "updated_at": "2022-02-10T15:26:28Z"
-      }
-    ],
-    "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
-    "total_count": 0,
-    "repository": {
-      "id": 1296269,
-      "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
-      "name": "Hello-World",
-      "full_name": "octocat/Hello-World",
-      "private": false,
-      "owner": {
-        "login": "octocat",
-        "id": 583231,
-        "node_id": "MDQ6VXNlcjU4MzIzMQ==",
-        "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
-        "gravatar_id": "",
-        "url": "https://api.github.com/users/octocat",
-        "html_url": "https://github.com/octocat",
-        "followers_url": "https://api.github.com/users/octocat/followers",
-        "following_url": "https://api.github.com/users/octocat/following{/other_user}",
-        "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
-        "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
-        "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
-        "organizations_url": "https://api.github.com/users/octocat/orgs",
-        "repos_url": "https://api.github.com/users/octocat/repos",
-        "events_url": "https://api.github.com/users/octocat/events{/privacy}",
-        "received_events_url": "https://api.github.com/users/octocat/received_events",
-        "type": "User",
-        "site_admin": false
-      },
-      "html_url": "https://github.com/octocat/Hello-World",
-      "description": "My first repository on GitHub!",
-      "fork": false,
-      "url": "https://api.github.com/repos/octocat/Hello-World",
-      "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
-      "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
-      "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
-      "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
-      "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
-      "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
-      "events_url": "https://api.github.com/repos/octocat/Hello-World/events",
-      "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
-      "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
-      "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
-      "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
-      "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
-      "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
-      "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
-      "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
-      "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
-      "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
-      "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
-      "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
-      "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
-      "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
-      "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
-      "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
-      "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
-      "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
-      "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
-      "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
-      "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
-      "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
-      "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
-      "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
-      "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
-      "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
-      "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
-      "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
-      "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments"
-    },
-    "commit_url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
-    "url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/status"
-  }
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-expected.json b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-expected.json
new file mode 100644
index 0000000000..ad84fee1f3
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-expected.json
@@ -0,0 +1,58 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": [
+              {
+                "context": "atlantis/apply"
+              },
+              {
+                "context": "my-required-expected-check"
+              }
+            ]
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": []
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-failed.json
new file mode 100644
index 0000000000..8b1ee9c1b5
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-failed.json
@@ -0,0 +1,64 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": [
+              {
+                "context": "atlantis/apply"
+              },
+              {
+                "context": "my-required-expected-check"
+              }
+            ]
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": []
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-required-expected-check",
+                        "state": "FAILED",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-passed.json b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-passed.json
new file mode 100644
index 0000000000..3dd40fcf2a
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/branch-protection-passed.json
@@ -0,0 +1,64 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": [
+              {
+                "context": "atlantis/apply"
+              },
+              {
+                "context": "my-required-expected-check"
+              }
+            ]
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": []
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-required-expected-check",
+                        "state": "SUCCESS",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/repository-id.json b/server/events/vcs/testdata/github-pull-request-mergeability/repository-id.json
new file mode 100644
index 0000000000..9e6d02e114
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/repository-id.json
@@ -0,0 +1,7 @@
+{
+  "data": {
+    "repository": {
+      "databaseId": 120519269
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-pending.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-pending.json
new file mode 100644
index 0000000000..4ce1799bcd
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-pending.json
@@ -0,0 +1,65 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-expected.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-expected.json
new file mode 100644
index 0000000000..d0914e6395
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-expected.json
@@ -0,0 +1,68 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed.json
new file mode 100644
index 0000000000..6bc4f55c25
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed.json
@@ -0,0 +1,74 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-required-expected-check",
+                        "state": "FAILED",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-neutral.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-neutral.json
new file mode 100644
index 0000000000..f6b3183780
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-neutral.json
@@ -0,0 +1,74 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my-required-expected-check",
+                        "conclusion": "NEUTRAL",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-passed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-passed.json
new file mode 100644
index 0000000000..95b7b5350c
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-passed.json
@@ -0,0 +1,74 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-required-expected-check",
+                        "state": "SUCCESS",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending.json
new file mode 100644
index 0000000000..60d70332de
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending.json
@@ -0,0 +1,74 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-required-expected-check",
+                        "state": "PENDING",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-skipped.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-skipped.json
new file mode 100644
index 0000000000..8427088fe0
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-skipped.json
@@ -0,0 +1,74 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    },
+                    {
+                      "context": "my-required-expected-check"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my-required-expected-check",
+                        "conclusion": "SKIPPED",
+                        "isRequired": true
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-evaluate-workflow-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-evaluate-workflow-failed.json
new file mode 100644
index 0000000000..ddde1d8ac5
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-evaluate-workflow-failed.json
@@ -0,0 +1,95 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "EVALUATE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": null
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my required (evaluate-enforcement) check",
+                        "conclusion": "FAILURE",
+                        "isRequired": true,
+                        "checkSuite": {
+                          "workflowRun": {
+                            "file": {
+                              "path": ".github/workflows/my-required-workflow.yaml",
+                              "repositoryFileUrl": "https://github.com/runatlantis/atlantis/blob/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.github/workflows/my-required-workflow.yaml",
+                              "repositoryName": "runatlantis/atlantis"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-check-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-check-failed.json
new file mode 100644
index 0000000000..6368cbe69e
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-check-failed.json
@@ -0,0 +1,71 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my-optional-check",
+                        "conclusion": "FAILURE",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-status-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-status-failed.json
new file mode 100644
index 0000000000..111ff4337a
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-optional-status-failed.json
@@ -0,0 +1,71 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "my-optional-check",
+                        "state": "FAILURE",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-expected.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-expected.json
new file mode 100644
index 0000000000..9b21af9390
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-expected.json
@@ -0,0 +1,80 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": null
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-failed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-failed.json
new file mode 100644
index 0000000000..570fdb9276
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-failed.json
@@ -0,0 +1,95 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": null
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my required check",
+                        "conclusion": "FAILURE",
+                        "isRequired": true,
+                        "checkSuite": {
+                          "workflowRun": {
+                            "file": {
+                              "path": ".github/workflows/my-required-workflow.yaml",
+                              "repositoryFileUrl": "https://github.com/runatlantis/atlantis/blob/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.github/workflows/my-required-workflow.yaml",
+                              "repositoryName": "runatlantis/atlantis"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-match.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-match.json
new file mode 100644
index 0000000000..e40b014ab3
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-match.json
@@ -0,0 +1,95 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my required check",
+                        "conclusion": "SUCCESS",
+                        "isRequired": true,
+                        "checkSuite": {
+                          "workflowRun": {
+                            "file": {
+                              "path": ".github/workflows/my-required-workflow.yaml",
+                              "repositoryFileUrl": "https://github.com/runatlantis/atlantis/blob/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.github/workflows/my-required-workflow.yaml",
+                              "repositoryName": "runatlantis/atlantis"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-mismatch.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-mismatch.json
new file mode 100644
index 0000000000..5a79ce3e33
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed-sha-mismatch.json
@@ -0,0 +1,95 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my required check",
+                        "conclusion": "SUCCESS",
+                        "isRequired": true,
+                        "checkSuite": {
+                          "workflowRun": {
+                            "file": {
+                              "path": ".github/workflows/my-required-workflow.yaml",
+                              "repositoryFileUrl": "https://github.com/runatlantis/atlantis/blob/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/.github/workflows/my-required-workflow.yaml",
+                              "repositoryName": "runatlantis/atlantis"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed.json
new file mode 100644
index 0000000000..6a88b4c8c5
--- /dev/null
+++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-workflow-passed.json
@@ -0,0 +1,95 @@
+{
+  "data": {
+    "repository": {
+      "pullRequest": {
+        "reviewDecision": null,
+        "baseRef": {
+          "branchProtectionRule": {
+            "requiredStatusChecks": []
+          },
+          "rules": {
+            "pageInfo": {
+              "endCursor": "QWERTY",
+              "hasNextPage": false
+            },
+            "nodes": [
+              {
+                "type": "REQUIRED_STATUS_CHECKS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "requiredStatusChecks": [
+                    {
+                      "context": "atlantis/apply"
+                    }
+                  ]
+                }
+              },
+              {
+                "type": "WORKFLOWS",
+                "repositoryRuleset": {
+                  "enforcement": "ACTIVE"
+                },
+                "parameters": {
+                  "workflows": [
+                    {
+                      "path": ".github/workflows/my-required-workflow.yaml",
+                      "repositoryId": 120519269,
+                      "sha": null
+                    }
+                  ]
+                }
+              }
+            ]
+          }
+        },
+        "commits": {
+          "nodes": [
+            {
+              "commit": {
+                "statusCheckRollup": {
+                  "contexts": {
+                    "pageInfo": {
+                      "endCursor": "QWERTY",
+                      "hasNextPage": false
+                    },
+                    "nodes": [
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/apply",
+                        "state": "PENDING",
+                        "isRequired": true
+                      },
+                      {
+                        "__typename": "StatusContext",
+                        "context": "atlantis/plan",
+                        "state": "SUCCESS",
+                        "isRequired": false
+                      },
+                      {
+                        "__typename": "CheckRun",
+                        "name": "my required check",
+                        "conclusion": "SUCCESS",
+                        "isRequired": true,
+                        "checkSuite": {
+                          "workflowRun": {
+                            "file": {
+                              "path": ".github/workflows/my-required-workflow.yaml",
+                              "repositoryFileUrl": "https://github.com/runatlantis/atlantis/blob/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/.github/workflows/my-required-workflow.yaml",
+                              "repositoryName": "runatlantis/atlantis"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
\ No newline at end of file