• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / integration-test-runner / 1681953125

21 Feb 2025 08:24AM UTC coverage: 59.965% (-4.4%) from 64.328%
1681953125

Pull #357

gitlab-ci

danielskinstad
feat: run integration pipeline on demand

Allow you to run the full integration pipeline on a protected branch
when a trusted member runs `start integration pipeline`.

As of now, this will only run with main - and you cannot specify PRs to
run the pipeline with.

Ticket: QA-863

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>
Pull Request #357: wip: integration tests on demand

77 of 311 new or added lines in 7 files covered. (24.76%)

2 existing lines in 1 file now uncovered.

1736 of 2895 relevant lines covered (59.97%)

2.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.92
/pullreq_pipeline.go
1
package main
2

3
import (
4
        "fmt"
5
        "os"
6
        "strconv"
7
        "strings"
8

9
        "github.com/google/go-github/v28/github"
10
        "github.com/sirupsen/logrus"
11
        gitlab "gitlab.com/gitlab-org/api/client-go"
12

13
        clientgitlab "github.com/mendersoftware/integration-test-runner/client/gitlab"
14
        "github.com/mendersoftware/integration-test-runner/git"
15
)
16

17
func startPRPipeline(
18
        log *logrus.Entry,
19
        ref string,
20
        event *github.PullRequestEvent,
21
        conf *config,
22
        isOrgMember func() bool,
23
) error {
1✔
24
        client, err := clientgitlab.NewGitLabClient(
1✔
25
                conf.gitlabToken,
1✔
26
                conf.gitlabBaseURL,
1✔
27
                conf.dryRunMode,
1✔
28
        )
1✔
29
        if err != nil {
1✔
30
                return err
×
31
        }
×
32
        pr := event.GetPullRequest()
1✔
33
        org := event.GetOrganization().GetLogin()
1✔
34
        head := pr.GetHead()
1✔
35
        base := pr.GetBase()
1✔
36
        repo := event.GetRepo()
1✔
37
        if repo.GetName() == "mender-qa" {
2✔
38
                // Verify that the pipe is started by a member of the organization
1✔
39
                if isOrgMember() {
2✔
40
                        log.Warnf(
1✔
41
                                "%s is making a pullrequest, but he/she is not a member of our organization, "+
1✔
42
                                        "ignoring",
1✔
43
                                pr.GetUser().GetLogin(),
1✔
44
                        )
1✔
45
                        return nil
1✔
46
                }
1✔
47
        }
48
        repoURL, err := getRemoteURLGitLab(org, repo.GetName())
1✔
49
        if err != nil {
1✔
50
                return err
×
51
        }
×
52
        repoHostURI := strings.SplitN(repoURL, ":", 2)
1✔
53
        if len(repoHostURI) != 2 {
1✔
54
                return fmt.Errorf("invalid GitLab URL '%s': failed to start GitLab pipeline", repoURL)
×
55
        }
×
56
        gitlabPath := repoHostURI[1]
1✔
57

1✔
58
        ciIIDKey := "CI_EXTERNAL_PULL_REQUEST_IID"
1✔
59
        ciIID := strconv.Itoa(event.GetNumber())
1✔
60
        ciSourceRepoKey := "CI_EXTERNAL_PULL_REQUEST_SOURCE_REPOSITORY"
1✔
61
        ciSourceRepo := head.GetRepo().GetFullName()
1✔
62
        ciTargetRepoKey := "CI_EXTERNAL_PULL_REQUEST_TARGET_REPOSITORY"
1✔
63
        ciTargetRepo := repo.GetFullName()
1✔
64
        ciSourceBranchNameKey := "CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME"
1✔
65
        ciSourceBranchName := head.GetRef()
1✔
66
        ciSourceBranchSHAKey := "CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_SHA"
1✔
67
        ciSourceBranchSHA := head.GetSHA()
1✔
68
        ciTargetBranchNameKey := "CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_NAME"
1✔
69
        ciTargetBranchName := base.GetRef()
1✔
70
        ciTargetBranchShaKey := "CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA"
1✔
71
        ciTargetBranchSha := base.GetSHA()
1✔
72

1✔
73
        pipeline, err := client.CreatePipeline(gitlabPath, &gitlab.CreatePipelineOptions{
1✔
74
                Ref: &ref,
1✔
75
                Variables: &[]*gitlab.PipelineVariableOptions{
1✔
76
                        {Key: &ciIIDKey, Value: &ciIID},
1✔
77
                        {Key: &ciSourceRepoKey, Value: &ciSourceRepo},
1✔
78
                        {Key: &ciTargetRepoKey, Value: &ciTargetRepo},
1✔
79
                        {Key: &ciSourceBranchNameKey, Value: &ciSourceBranchName},
1✔
80
                        {Key: &ciSourceBranchSHAKey, Value: &ciSourceBranchSHA},
1✔
81
                        {Key: &ciTargetBranchNameKey, Value: &ciTargetBranchName},
1✔
82
                        {Key: &ciTargetBranchShaKey, Value: &ciTargetBranchSha},
1✔
83
                },
1✔
84
        })
1✔
85
        if err != nil {
1✔
86
                return err
×
87
        } else {
1✔
88
                log.Debugf("started pipeline for PR: %s", pipeline.WebURL)
1✔
89
        }
1✔
90

91
        return nil
1✔
92
}
93

94
func syncPullRequestBranch(
95
        log *logrus.Entry,
96
        pr *github.PullRequestEvent,
97
        conf *config,
98
) (string, error) {
1✔
99
        prBranchName := "pr_" + strconv.Itoa(pr.GetNumber())
1✔
100
        if err := syncBranch(prBranchName, log, pr, conf); err != nil {
1✔
NEW
101
                mainErrMsg := "There was an error syncing branches"
×
NEW
102
                return "", fmt.Errorf("%v returned error: %s: %s", err, mainErrMsg, err.Error())
×
NEW
103
        }
×
104
        return prBranchName, nil
1✔
105
}
106

107
func syncBranch(
108
        prBranchName string,
109
        log *logrus.Entry,
110
        pr *github.PullRequestEvent,
111
        conf *config,
112
) error {
1✔
113

1✔
114
        repo := pr.GetRepo().GetName()
1✔
115
        org := pr.GetOrganization().GetLogin()
1✔
116
        prNum := strconv.Itoa(pr.GetNumber())
1✔
117

1✔
118
        tmpdir, err := os.MkdirTemp("", repo)
1✔
119
        if err != nil {
1✔
NEW
120
                return err
×
121
        }
×
122
        defer os.RemoveAll(tmpdir)
1✔
123

1✔
124
        gitcmd := git.Command("init", ".")
1✔
125
        gitcmd.Dir = tmpdir
1✔
126
        out, err := gitcmd.CombinedOutput()
1✔
127
        if err != nil {
1✔
NEW
128
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
129
        }
×
130

131
        repoURL := getRemoteURLGitHub(conf.githubProtocol, conf.githubOrganization, repo)
1✔
132
        gitcmd = git.Command("remote", "add", "github", repoURL)
1✔
133
        gitcmd.Dir = tmpdir
1✔
134
        out, err = gitcmd.CombinedOutput()
1✔
135
        if err != nil {
1✔
NEW
136
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
137
        }
×
138

139
        remoteURL, err := getRemoteURLGitLab(org, repo)
1✔
140
        if err != nil {
1✔
NEW
141
                return fmt.Errorf("getRemoteURLGitLab returned error: %s", err.Error())
×
142
        }
×
143

144
        gitcmd = git.Command("remote", "add", "gitlab", remoteURL)
1✔
145
        gitcmd.Dir = tmpdir
1✔
146
        out, err = gitcmd.CombinedOutput()
1✔
147
        if err != nil {
1✔
NEW
148
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
149
        }
×
150

151
        gitcmd = git.Command("fetch", "github", "pull/"+prNum+"/head:"+prBranchName)
1✔
152
        gitcmd.Dir = tmpdir
1✔
153
        out, err = gitcmd.CombinedOutput()
1✔
154
        if err != nil {
1✔
NEW
155
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
156
        }
×
157

158
        // Push but not don't trigger CI (yet)
159
        gitcmd = git.Command("push", "-f", "-o", "ci.skip", "--set-upstream", "gitlab", prBranchName)
1✔
160
        gitcmd.Dir = tmpdir
1✔
161
        out, err = gitcmd.CombinedOutput()
1✔
162
        if err != nil {
1✔
NEW
163
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
164
        }
×
165

166
        log.Infof("Created branch: %s:%s", repo, prBranchName)
1✔
167
        return nil
1✔
168
}
169

170
func deleteStaleGitlabPRBranch(log *logrus.Entry, pr *github.PullRequestEvent, conf *config) error {
1✔
171

1✔
172
        // If the action is "closed" the pull request was merged or just closed,
1✔
173
        // stop builds in both cases.
1✔
174
        if pr.GetAction() != "closed" {
1✔
175
                log.Debugf("deleteStaleGitlabPRBranch: PR not closed, therefore not stopping it's pipeline")
×
176
                return nil
×
177
        }
×
178
        repoName := pr.GetRepo().GetName()
1✔
179
        repoOrg := pr.GetOrganization().GetLogin()
1✔
180

1✔
181
        tmpdir, err := os.MkdirTemp("", repoName)
1✔
182
        if err != nil {
1✔
183
                return err
×
184
        }
×
185
        defer os.RemoveAll(tmpdir)
1✔
186

1✔
187
        gitcmd := git.Command("init", ".")
1✔
188
        gitcmd.Dir = tmpdir
1✔
189
        out, err := gitcmd.CombinedOutput()
1✔
190
        if err != nil {
1✔
191
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
192
        }
×
193

194
        remoteURL, err := getRemoteURLGitLab(repoOrg, repoName)
1✔
195
        if err != nil {
1✔
196
                return fmt.Errorf("getRemoteURLGitLab returned error: %s", err.Error())
×
197
        }
×
198

199
        gitcmd = git.Command("remote", "add", "gitlab", remoteURL)
1✔
200
        gitcmd.Dir = tmpdir
1✔
201
        out, err = gitcmd.CombinedOutput()
1✔
202
        if err != nil {
1✔
203
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
204
        }
×
205

206
        gitcmd = git.Command("fetch", "gitlab")
1✔
207
        gitcmd.Dir = tmpdir
1✔
208
        out, err = gitcmd.CombinedOutput()
1✔
209
        if err != nil {
1✔
210
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
211
        }
×
212

213
        gitcmd = git.Command("push", "gitlab", "--delete", fmt.Sprintf("pr_%d", pr.GetNumber()))
1✔
214
        gitcmd.Dir = tmpdir
1✔
215
        out, err = gitcmd.CombinedOutput()
1✔
216
        if err != nil {
1✔
217
                return fmt.Errorf("%v returned error: %s: %s", gitcmd.Args, out, err.Error())
×
218
        }
×
219

220
        return nil
1✔
221

222
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc