summaryrefslogtreecommitdiffstats
path: root/src/qtmoduleupdater/gerrit.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/qtmoduleupdater/gerrit.go')
-rw-r--r--src/qtmoduleupdater/gerrit.go260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/qtmoduleupdater/gerrit.go b/src/qtmoduleupdater/gerrit.go
new file mode 100644
index 00000000..d3ef67f5
--- /dev/null
+++ b/src/qtmoduleupdater/gerrit.go
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the repo tools module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "net"
+ "net/url"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// GerritPatchSet corresponds to the patch set JSON object returned by Gerrit's JSON API.
+type GerritPatchSet struct {
+ Number int `json:"number"`
+ Revision string `json:"revision"`
+ Parents []string `json:"parents"`
+ Uploader json.RawMessage `json:"uploader"`
+ CreatedOn uint `json:"createdOn"`
+ Author json.RawMessage `json:"author"`
+ SizeInsertions int `json:"sizeInsertions"`
+ SizeDeletions int `json:"sizeDeletions"`
+}
+
+// GerritChangeOrStats corresponds to the JSON data returned by Gerrit's Query JSON API.
+type GerritChangeOrStats struct {
+ Type string `json:"type"`
+ RowCount int `json:"rowCount"`
+ RunTimeMilliseconds int `json:"runTimeMilliseconds"`
+
+ Project string `json:"project"`
+ Branch string `json:"branch"`
+ ID string `json:"id"`
+ Number int `json:"number"`
+ Subject string `json:"subject"`
+ Owner json.RawMessage `json:"owner"`
+ URL string `json:"url"`
+ CreatedOn uint `json:"createdOn"`
+ LastUpdated uint `json:"lastUpdated"`
+ SortKey string `json:"sortKey"`
+ Open bool `json:"open"`
+ Status string `json:"status"`
+ PatchSets []GerritPatchSet `json:"patchSets"`
+}
+
+func gerritSSHCommand(gerritURL url.URL, arguments ...string) (*exec.Cmd, error) {
+ user := os.Getenv("GIT_SSH_USER")
+ if user != "" {
+ gerritURL.User = url.User(user)
+ }
+
+ host, port, err := net.SplitHostPort(gerritURL.Host)
+ if err != nil {
+ return nil, fmt.Errorf("Error splitting host and port from gerrit URL: %s", err)
+ }
+
+ userAtHost := host
+ if gerritURL.User != nil {
+ userAtHost = gerritURL.User.Username() + "@" + host
+ }
+
+ newArgs := []string{"-oBatchMode=yes", userAtHost, "-p", port}
+ newArgs = append(newArgs, arguments...)
+ ssh := os.Getenv("GIT_SSH")
+ if ssh == "" {
+ ssh = "ssh"
+ }
+ log.Printf("Running gerrit ssh command: 'ssh %v'\n", newArgs)
+ return exec.Command(ssh, newArgs...), nil
+}
+
+func getGerritChangeStatus(project string, branch string, changeID string) (status string, err error) {
+ gerritURL, err := RepoURL(project)
+ if err != nil {
+ return "", fmt.Errorf("Error parsing gerrit URL: %s", err)
+ }
+ queryString := fmt.Sprintf(`project:%s branch:%s %s`, project, branch, changeID)
+ cmd, err := gerritSSHCommand(*gerritURL, "gerrit", "query", "--patch-sets", "--format JSON", queryString)
+ if err != nil {
+ return "", err
+ }
+ output, err := cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("Error running gerrit query command: %s", err)
+ }
+
+ var id string
+
+ for _, line := range strings.Split(string(output), "\n") {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+
+ var field GerritChangeOrStats
+ err = json.Unmarshal([]byte(line), &field)
+ if err != nil {
+ return "", fmt.Errorf("Error reading gerrit json response: %s:%s", err, string(output))
+ }
+ if field.Type == "stats" {
+ if field.RowCount != 1 {
+ return "", fmt.Errorf("unexpected row count %v when querying for existing gerrit change", field.RowCount)
+ }
+ continue
+ }
+
+ if field.Project != project {
+ return "", fmt.Errorf("unexpectedly found change for a different project. Received %s, expected %s for %s", field.Project, project, changeID)
+ }
+ if id != "" {
+ return "", fmt.Errorf("unexpectedly found multiple changes for change ID %s", changeID)
+ }
+ id = field.ID
+ status = field.Status
+ }
+ return status, nil
+}
+
+func getExistingChange(project string, branch string) (gerritChangeID string, changeNumber int, patchSetNr int, err error) {
+ gerritURL, err := RepoURL(project)
+ if err != nil {
+ return "", 0, 0, fmt.Errorf("Error parsing gerrit URL: %s", err)
+ }
+ queryString := fmt.Sprintf(`project:%s branch:%s status:open owner:self message:{Update dependencies on \'%s\' in %s}`, project, branch, branch, project)
+ cmd, err := gerritSSHCommand(*gerritURL, "gerrit", "query", "--patch-sets", "--format JSON", queryString)
+ if err != nil {
+ return "", 0, 0, err
+ }
+ output, err := cmd.Output()
+ if err != nil {
+ return "", 0, 0, fmt.Errorf("Error running gerrit query command: %s", err)
+ }
+
+ var id string
+
+ for _, line := range strings.Split(string(output), "\n") {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+
+ var field GerritChangeOrStats
+ err = json.Unmarshal([]byte(line), &field)
+ if err != nil {
+ return "", 0, 0, fmt.Errorf("Error reading gerrit json response: %s:%s", err, string(output))
+ }
+ if field.Type == "stats" {
+ if field.RowCount == 0 {
+ return "", 0, 0, nil
+ }
+ if field.RowCount != 1 {
+ return "", 0, 0, fmt.Errorf("unexpected row count %v when querying for existing gerrit changes", field.RowCount)
+ }
+ continue
+ }
+
+ if field.Project == project {
+ if id != "" {
+ return "", 0, 0, fmt.Errorf("unexpectedly found multiple changes for submodule updates: Id %s and %s", id, field.ID)
+ }
+ id = field.ID
+ changeNumber = field.Number
+ patchSetNr = 0
+ for _, patchSet := range field.PatchSets {
+ if patchSet.Number > patchSetNr {
+ patchSetNr = patchSet.Number
+ }
+ }
+ continue
+ }
+ }
+ return id, changeNumber, patchSetNr, nil
+}
+
+func escapeGerritMessage(message string) string {
+ replacer := strings.NewReplacer(`\`, `\\`, `"`, `\"`, `'`, `\'`)
+ return `"` + replacer.Replace(message) + `"`
+}
+
+func pushAndStageChange(repoPath string, branch string, commitID OID, summary string, pushUserName string, manualStage bool) error {
+ repo, err := OpenRepository(repoPath)
+ if err != nil {
+ return err
+ }
+
+ pushURL, err := RepoURL(repoPath)
+ if err != nil {
+ return err
+ }
+ if pushUserName != "" {
+ pushURL.User = url.User(pushUserName)
+ }
+
+ err = repo.Push(pushURL, commitID, "refs/for/"+branch)
+ if err != nil {
+ return err
+ }
+
+ reviewArgs := []string{"gerrit", "review", string(commitID)}
+
+ if summary != "" {
+ reviewArgs = append(reviewArgs, "-m", escapeGerritMessage(summary))
+ }
+ // Pass in sanity review, since the sanity bot runs only after a delay and thus the commit will get refused.
+ reviewArgs = append(reviewArgs, "--code-review", "2", "--sanity-review", "1")
+
+ updateCommand, err := gerritSSHCommand(*pushURL, reviewArgs...)
+ if err != nil {
+ return err
+ }
+ updateCommand.Stdout = os.Stdout
+ updateCommand.Stderr = os.Stderr
+ if err = updateCommand.Run(); err != nil {
+ return err
+ }
+
+ if manualStage {
+ return nil
+ }
+
+ stageArgs := []string{"gerrit-plugin-qt-workflow", "stage", string(commitID)}
+ updateCommand, err = gerritSSHCommand(*pushURL, stageArgs...)
+ if err != nil {
+ return err
+ }
+ updateCommand.Stdout = os.Stdout
+ updateCommand.Stderr = os.Stderr
+ if err = updateCommand.Run(); err != nil {
+ return err
+ }
+ return nil
+}