Skip to content

Commit 1a7cc36

Browse files
authored
feat: add a pending status for apply when running plan command (#2053)
* add a pending status for apply when running plan command * fix status updater logic, we should only update the status after we check if the PR is mergeable like the doc next to it says * do not dismiss the PR if the only our "atlantis/apply" status is pending/failing * fix logic and tests * linting
1 parent 534ccb0 commit 1a7cc36

6 files changed

+297
-5
lines changed

server/events/apply_command_runner.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ func (a *ApplyCommandRunner) Run(ctx *CommandContext, cmd *CommentCommand) {
9393
return
9494
}
9595

96-
if err = a.commitStatusUpdater.UpdateCombined(baseRepo, pull, models.PendingCommitStatus, cmd.CommandName()); err != nil {
97-
ctx.Log.Warn("unable to update commit status: %s", err)
98-
}
99-
10096
// Get the mergeable status before we set any build statuses of our own.
10197
// We do this here because when we set a "Pending" status, if users have
10298
// required the Atlantis status checks to pass, then we've now changed
@@ -111,6 +107,10 @@ func (a *ApplyCommandRunner) Run(ctx *CommandContext, cmd *CommentCommand) {
111107
ctx.Log.Warn("unable to get pull request status: %s. Continuing with mergeable and approved assumed false", err)
112108
}
113109

110+
if err = a.commitStatusUpdater.UpdateCombined(baseRepo, pull, models.PendingCommitStatus, cmd.CommandName()); err != nil {
111+
ctx.Log.Warn("unable to update commit status: %s", err)
112+
}
113+
114114
var projectCmds []models.ProjectCommandContext
115115
projectCmds, err = a.prjCmdBuilder.BuildApplyCommands(ctx, cmd)
116116

server/events/plan_command_runner.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ func (p *PlanCommandRunner) runAutoplan(ctx *CommandContext) {
102102

103103
// At this point we are sure Atlantis has work to do, so set commit status to pending
104104
if err := p.commitStatusUpdater.UpdateCombined(ctx.Pull.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.PlanCommand); err != nil {
105-
ctx.Log.Warn("unable to update commit status: %s", err)
105+
ctx.Log.Warn("unable to update plan commit status: %s", err)
106+
}
107+
if err := p.commitStatusUpdater.UpdateCombinedCount(ctx.Pull.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.ApplyCommand, 0, len(projectCmds)); err != nil {
108+
ctx.Log.Warn("unable to update apply commit status: %s", err)
106109
}
107110

108111
// Only run commands in parallel if enabled
@@ -179,6 +182,14 @@ func (p *PlanCommandRunner) run(ctx *CommandContext, cmd *CommentCommand) {
179182

180183
projectCmds, policyCheckCmds := p.partitionProjectCmds(ctx, projectCmds)
181184

185+
// At this point we are sure Atlantis has work to do, so set commit status to pending
186+
if err := p.commitStatusUpdater.UpdateCombined(ctx.Pull.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.PlanCommand); err != nil {
187+
ctx.Log.Warn("unable to update plan commit status: %s", err)
188+
}
189+
if err := p.commitStatusUpdater.UpdateCombinedCount(ctx.Pull.BaseRepo, ctx.Pull, models.PendingCommitStatus, models.ApplyCommand, 0, len(projectCmds)); err != nil {
190+
ctx.Log.Warn("unable to update apply commit status: %s", err)
191+
}
192+
182193
// Only run commands in parallel if enabled
183194
var result CommandResult
184195
if p.isParallelEnabled(projectCmds) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"state": "pending",
3+
"statuses": [
4+
5+
],
6+
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
7+
"total_count": 0,
8+
"repository": {
9+
"id": 1296269,
10+
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
11+
"name": "Hello-World",
12+
"full_name": "octocat/Hello-World",
13+
"private": false,
14+
"owner": {
15+
"login": "octocat",
16+
"id": 583231,
17+
"node_id": "MDQ6VXNlcjU4MzIzMQ==",
18+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
19+
"gravatar_id": "",
20+
"url": "https://api.github.com/users/octocat",
21+
"html_url": "https://github.com/octocat",
22+
"followers_url": "https://api.github.com/users/octocat/followers",
23+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
24+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
25+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
26+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
27+
"organizations_url": "https://api.github.com/users/octocat/orgs",
28+
"repos_url": "https://api.github.com/users/octocat/repos",
29+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
30+
"received_events_url": "https://api.github.com/users/octocat/received_events",
31+
"type": "User",
32+
"site_admin": false
33+
},
34+
"html_url": "https://github.com/octocat/Hello-World",
35+
"description": "My first repository on GitHub!",
36+
"fork": false,
37+
"url": "https://api.github.com/repos/octocat/Hello-World",
38+
"forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
39+
"keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
40+
"collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
41+
"teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
42+
"hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
43+
"issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
44+
"events_url": "https://api.github.com/repos/octocat/Hello-World/events",
45+
"assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
46+
"branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
47+
"tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
48+
"blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
49+
"git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
50+
"git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
51+
"trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
52+
"statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
53+
"languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
54+
"stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
55+
"contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
56+
"subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
57+
"subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
58+
"commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
59+
"git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
60+
"comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
61+
"issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
62+
"contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
63+
"compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
64+
"merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
65+
"archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
66+
"downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
67+
"issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
68+
"pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
69+
"milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
70+
"notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
71+
"labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
72+
"releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
73+
"deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments"
74+
},
75+
"commit_url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
76+
"url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/status"
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
{
2+
"state": "blocked",
3+
"statuses": [
4+
{
5+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
6+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
7+
"id": 16230299674,
8+
"node_id": "SC_kwDOFRFvL88AAAADx2a4Gg",
9+
"state": "success",
10+
"description": "Plan succeeded.",
11+
"target_url": "https://localhost/jobs/octocat/Hello-World/1/project1",
12+
"context": "atlantis/plan: project1",
13+
"created_at": "2022-02-10T15:26:01Z",
14+
"updated_at": "2022-02-10T15:26:01Z"
15+
},
16+
{
17+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
18+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
19+
"id": 16230303174,
20+
"node_id": "SC_kwDOFRFvL88AAAADx2bFxg",
21+
"state": "success",
22+
"description": "Plan succeeded.",
23+
"target_url": "https://localhost/jobs/octocat/Hello-World/1/project2",
24+
"context": "atlantis/plan: project2",
25+
"created_at": "2022-02-10T15:26:12Z",
26+
"updated_at": "2022-02-10T15:26:12Z"
27+
},
28+
{
29+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
30+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
31+
"id": 16230303679,
32+
"node_id": "SC_kwDOFRFvL88AAAADx2bHvw",
33+
"state": "success",
34+
"description": "2/2 projects planned successfully.",
35+
"target_url": "",
36+
"context": "atlantis/plan",
37+
"created_at": "2022-02-10T15:26:13Z",
38+
"updated_at": "2022-02-10T15:26:13Z"
39+
},
40+
{
41+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
42+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
43+
"id": 16230307923,
44+
"node_id": "SC_kwDOFRFvL88AAAADx2bYUw",
45+
"state": "failure",
46+
"description": "Apply failed.",
47+
"target_url": "https://localhost/jobs/octocat/Hello-World/1/project1",
48+
"context": "atlantis/apply: project1",
49+
"created_at": "2022-02-10T15:26:27Z",
50+
"updated_at": "2022-02-10T15:26:27Z"
51+
},
52+
{
53+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
54+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
55+
"id": 16230308153,
56+
"node_id": "SC_kwDOFRFvL88AAAADx2bZOQ",
57+
"state": "failure",
58+
"description": "Apply failed.",
59+
"target_url": "https://localhost/jobs/octocat/Hello-World/1/project2",
60+
"context": "atlantis/apply: project2",
61+
"created_at": "2022-02-10T15:26:27Z",
62+
"updated_at": "2022-02-10T15:26:27Z"
63+
},
64+
{
65+
"url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
66+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
67+
"id": 16230308528,
68+
"node_id": "SC_kwDOFRFvL88AAAADx2basA",
69+
"state": "failure",
70+
"description": "0/2 projects applied successfully.",
71+
"target_url": "",
72+
"context": "atlantis/apply",
73+
"created_at": "2022-02-10T15:26:28Z",
74+
"updated_at": "2022-02-10T15:26:28Z"
75+
}
76+
],
77+
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
78+
"total_count": 0,
79+
"repository": {
80+
"id": 1296269,
81+
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
82+
"name": "Hello-World",
83+
"full_name": "octocat/Hello-World",
84+
"private": false,
85+
"owner": {
86+
"login": "octocat",
87+
"id": 583231,
88+
"node_id": "MDQ6VXNlcjU4MzIzMQ==",
89+
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
90+
"gravatar_id": "",
91+
"url": "https://api.github.com/users/octocat",
92+
"html_url": "https://github.com/octocat",
93+
"followers_url": "https://api.github.com/users/octocat/followers",
94+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
95+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
96+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
97+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
98+
"organizations_url": "https://api.github.com/users/octocat/orgs",
99+
"repos_url": "https://api.github.com/users/octocat/repos",
100+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
101+
"received_events_url": "https://api.github.com/users/octocat/received_events",
102+
"type": "User",
103+
"site_admin": false
104+
},
105+
"html_url": "https://github.com/octocat/Hello-World",
106+
"description": "My first repository on GitHub!",
107+
"fork": false,
108+
"url": "https://api.github.com/repos/octocat/Hello-World",
109+
"forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
110+
"keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
111+
"collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
112+
"teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
113+
"hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
114+
"issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
115+
"events_url": "https://api.github.com/repos/octocat/Hello-World/events",
116+
"assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
117+
"branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
118+
"tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
119+
"blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
120+
"git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
121+
"git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
122+
"trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
123+
"statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
124+
"languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
125+
"stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
126+
"contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
127+
"subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
128+
"subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
129+
"commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
130+
"git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
131+
"comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
132+
"issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
133+
"contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
134+
"compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
135+
"merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
136+
"archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
137+
"downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
138+
"issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
139+
"pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
140+
"milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
141+
"notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
142+
"labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
143+
"releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
144+
"deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments"
145+
},
146+
"commit_url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
147+
"url": "https://api.github.com/repos/octocat/Hello-World/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e/status"
148+
}

server/events/vcs/github_client.go

+28
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ func (g *GithubClient) PullIsMergeable(repo models.Repo, pull models.PullRequest
290290
return false, errors.Wrap(err, "getting pull request")
291291
}
292292
state := githubPR.GetMergeableState()
293+
status, _ := g.GetCombinedStatus(repo, githubPR.GetHead().GetSHA())
294+
293295
// We map our mergeable check to when the GitHub merge button is clickable.
294296
// This corresponds to the following states:
295297
// clean: No conflicts, all requirements satisfied.
@@ -299,9 +301,27 @@ func (g *GithubClient) PullIsMergeable(repo models.Repo, pull models.PullRequest
299301
// has_hooks: GitHub Enterprise only, if a repo has custom pre-receive
300302
// hooks. Merging is allowed (green box).
301303
// See: https://github.com/octokit/octokit.net/issues/1763
304+
//
305+
// We should not dismiss the PR if the only our "atlantis/apply" status is pending/failing
306+
if state == "blocked" {
307+
applyStatus := false
308+
for _, s := range status.Statuses {
309+
if strings.Contains(s.GetContext(), "atlantis/apply") {
310+
applyStatus = true
311+
continue
312+
}
313+
if s.GetContext() != "atlantis/apply" && s.GetState() != "success" {
314+
// If any other status is pending/failing mark as non-mergeable
315+
return false, nil
316+
}
317+
}
318+
return applyStatus, nil
319+
}
320+
302321
if state != "clean" && state != "unstable" && state != "has_hooks" {
303322
return false, nil
304323
}
324+
305325
return true, nil
306326
}
307327

@@ -332,6 +352,14 @@ func (g *GithubClient) GetPullRequest(repo models.Repo, num int) (*github.PullRe
332352
return pull, err
333353
}
334354

355+
func (g *GithubClient) GetCombinedStatus(repo models.Repo, ref string) (*github.CombinedStatus, error) {
356+
opts := github.ListOptions{
357+
PerPage: 300,
358+
}
359+
status, _, err := g.client.Repositories.GetCombinedStatus(g.ctx, repo.Owner, repo.Name, ref, &opts)
360+
return status, err
361+
}
362+
335363
// UpdateStatus updates the status badge on the pull request.
336364
// See https://github.com/blog/1227-commit-status-api.
337365
func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state models.CommitStatus, src string, description string, url string) error {

0 commit comments

Comments
 (0)