diff options
author | Florian Klink <flokli@flokli.de> | 2023-10-20T12·18+0100 |
---|---|---|
committer | flokli <flokli@flokli.de> | 2023-10-30T09·07+0000 |
commit | 9a1e5cf4c731c082032285e9b02e453fd0d11483 (patch) | |
tree | 7c6a265f3ee80146275a621154196d1f31fa170a /third_party | |
parent | 2513ddd2b7559628a0135ec0c8e4e8ea4444af8d (diff) |
chore(third_party/gerrit-queue): move to tvl overlay r/6906
Bump to a version including https://github.com/flokli/gerrit-queue/pull/15 Change-Id: Ie316498ca2c608e5489901c5705ce5f2dc047f29 Reviewed-on: https://cl.tvl.fyi/c/depot/+/9808 Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
Diffstat (limited to 'third_party')
20 files changed, 19 insertions, 1584 deletions
diff --git a/third_party/gerrit-queue/.buildkite/build.sh b/third_party/gerrit-queue/.buildkite/build.sh deleted file mode 100755 index 0a218c817ee5..000000000000 --- a/third_party/gerrit-queue/.buildkite/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -export GOPATH=~/go -go generate -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -a -ldflags '-extldflags \"-static\"' -o gerrit-queue diff --git a/third_party/gerrit-queue/.buildkite/pipeline.yml b/third_party/gerrit-queue/.buildkite/pipeline.yml deleted file mode 100644 index 0885cb9694ee..000000000000 --- a/third_party/gerrit-queue/.buildkite/pipeline.yml +++ /dev/null @@ -1,13 +0,0 @@ -steps: - - command: | - . /var/lib/buildkite-agent/.nix-profile/etc/profile.d/nix.sh - # produces a ./gerrit-queue - nix-shell --run ./.buildkite/build.sh - - mkdir -p out - mv ./gerrit-queue out/gerrit-queue-$(git describe --tags) - - label: "Build (linux/amd64)" - timeout: 30 - artifact_paths: - - "out/*" diff --git a/third_party/gerrit-queue/.gitignore b/third_party/gerrit-queue/.gitignore deleted file mode 100644 index f2ec770e3c34..000000000000 --- a/third_party/gerrit-queue/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.vscode -/statik -/.envrc.private -/gerrit-queue diff --git a/third_party/gerrit-queue/LICENSE b/third_party/gerrit-queue/LICENSE deleted file mode 100644 index 261eeb9e9f8b..000000000000 --- a/third_party/gerrit-queue/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/third_party/gerrit-queue/README.md b/third_party/gerrit-queue/README.md deleted file mode 100644 index 9ffb81b8d26d..000000000000 --- a/third_party/gerrit-queue/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# gerrit-queue - -This daemon automatically rebases and submits changesets from a Gerrit -instance, ensuring they still pass CI. - -In a usual gerrit setup with a linear master history, different developers -await CI feedback on a rebased changeset, then one clicks submit, and -effectively makes everybody else rebase again. `gerrit-queue` is meant to -remove these races to master. - -Developers can set the `Autosubmit` label to `+1` on all changesets in a series, -and if all preconditions on are met ("submittable" in gerrit speech, this -usually means passing CI and passing Code Review), `gerrit-queue` takes care of -rebasing and submitting it to master - -## How it works -Gerrit only knows about Changesets (and some relations to other changesets), -but usually developers think in terms of multiple changesets. - -### Fetching changesets -`gerrit-queue` fetches all changesets from gerrit, and tries to identify these -chains of changesets. We call them `Series`. All changesets need to have strict -parent/child relationships to be detected (so if only half of the stack gets -rebased by the Gerrit Web interface, these are considered individual series. - -Series are sorted by the number of changesets in them. This ensures longer -series are merged faster, and less rebases are triggered. In the future, this -might be extended to other metrics. - -### Submitting changesets -The submitqueue has a Trigger() function, which gets periodically executed. - -It can keep a reference to one single serie across multiple runs. This is -necessary if it previously rebased one serie to current HEAD and needs to wait -some time until CI feedback is there. If it wouldn't keep that state, it would -pick another series (with +1 from CI) and trigger a rebase on that one, so -depending on CI run times and trigger intervals, if not keepig this information -it'd end up rebasing all unrebased changesets on the same HEAD, and then just -pick one, instead of waiting for the one to finish. - -The Trigger() function first instructs the gerrit client to fetch changesets -and assemble series. -If there is a `wipSerie` from a previous run, we check if it can still be found -in the newly assembled list of series (it still needs to contain the same -number of series. Commit IDs may differ, because the code doesn't reassemble a -`wipSerie` after scheduling a rebase. -If the `wipSerie` could be refreshed, we update the pointer with the newly -assembled series. If we couldn't find it, we drop it. - -Now, we enter the main for loop. The first half of the loop checks various -conditions of the current `wipSerie`, and if successful, does the submit -("Submit phase"), the second half will pick a suitable new `wipSerie`, and -potentially do a rebase ("Pick phase"). - -#### Submit phase -We check if there is an existing `wipSerie`. If there isn't, we immediately go to -the "pick" phase. - -The `wipSerie` still needs to be rebased on `HEAD` (otherwise, the submit queue -advanced outside of gerrit), and should not fail CI (logical merge conflict) - -otherwise we discard it, and continue with the picking phase. - -If the `wipSerie` still contains a changeset awaiting CI feedback, we `return` -from the `Trigger()` function (and go back to sleep). - -If the changeset is "submittable" in gerrit speech, and has the necessary -submit queue tag set, we submit it. - -#### Pick phase -The pick phase finds a new `wipSerie`. It'll first try to find one that already -is rebased on the current `HEAD` (so the loop can just continue, and the next -submit phase simply submit), and otherwise fall back to a not-yet-rebased -serie. Because the rebase mandates waiting for CI, the code `return`s the -`Trigger()` function, so it'll be called again after waiting some time. - -## Compile and Run -```sh -go generate -GERRIT_PASSWORD=mypassword go run main.go --url https://gerrit.mydomain.com --username myuser --project myproject -``` diff --git a/third_party/gerrit-queue/default.nix b/third_party/gerrit-queue/default.nix deleted file mode 100644 index d3988f59d125..000000000000 --- a/third_party/gerrit-queue/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ pkgs, lib, ... }: - -pkgs.buildGoModule { - pname = "gerrit-queue"; - version = "master"; - vendorHash = "sha256:0n5h7j416yb2mwic9c3rhqza64jlvl7iw507r9mkw3jadn4whm7a"; - src = ./.; - - meta = with lib; { - description = "Gerrit submit bot"; - homepage = "https://github.com/tweag/gerrit-queue"; - license = licenses.asl20; - }; -} diff --git a/third_party/gerrit-queue/frontend/frontend.go b/third_party/gerrit-queue/frontend/frontend.go deleted file mode 100644 index 2cc65423f0e6..000000000000 --- a/third_party/gerrit-queue/frontend/frontend.go +++ /dev/null @@ -1,113 +0,0 @@ -package frontend - -import ( - "embed" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "html/template" - - "github.com/apex/log" - - "github.com/tweag/gerrit-queue/gerrit" - "github.com/tweag/gerrit-queue/misc" - "github.com/tweag/gerrit-queue/submitqueue" -) - -//go:embed templates -var templates embed.FS - -//loadTemplate loads a list of templates, relative to the templates root, and a -//FuncMap, and returns a template object -func loadTemplate(templateNames []string, funcMap template.FuncMap) (*template.Template, error) { - if len(templateNames) == 0 { - return nil, fmt.Errorf("templateNames can't be empty") - } - tmpl := template.New(templateNames[0]).Funcs(funcMap) - - for _, templateName := range templateNames { - r, err := templates.Open("/" + templateName) - if err != nil { - return nil, err - } - defer r.Close() - contents, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - tmpl, err = tmpl.Parse(string(contents)) - if err != nil { - return nil, err - } - } - - return tmpl, nil -} - -// MakeFrontend returns a http.Handler -func MakeFrontend(rotatingLogHandler *misc.RotatingLogHandler, gerritClient *gerrit.Client, runner *submitqueue.Runner) http.Handler { - projectName := gerritClient.GetProjectName() - branchName := gerritClient.GetBranchName() - - mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { - var wipSerie *gerrit.Serie = nil - HEAD := "" - currentlyRunning := runner.IsCurrentlyRunning() - - // don't trigger operations requiring a lock - if !currentlyRunning { - wipSerie = runner.GetWIPSerie() - HEAD = gerritClient.GetHEAD() - } - - funcMap := template.FuncMap{ - "changesetURL": func(changeset *gerrit.Changeset) string { - return gerritClient.GetChangesetURL(changeset) - }, - "levelToClasses": func(level log.Level) string { - switch level { - case log.DebugLevel: - return "text-muted" - case log.InfoLevel: - return "text-info" - case log.WarnLevel: - return "text-warning" - case log.ErrorLevel: - return "text-danger" - case log.FatalLevel: - return "text-danger" - default: - return "text-white" - } - }, - "fieldsToJSON": func(fields log.Fields) string { - jsonData, _ := json.Marshal(fields) - return string(jsonData) - }, - } - - tmpl := template.Must(loadTemplate([]string{ - "index.tmpl.html", - "serie.tmpl.html", - "changeset.tmpl.html", - }, funcMap)) - - tmpl.ExecuteTemplate(w, "index.tmpl.html", map[string]interface{}{ - // Config - "projectName": projectName, - "branchName": branchName, - - // State - "currentlyRunning": currentlyRunning, - "wipSerie": wipSerie, - "HEAD": HEAD, - - // History - "memory": rotatingLogHandler, - }) - }) - return mux -} diff --git a/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html b/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html deleted file mode 100644 index 5d3997885c73..000000000000 --- a/third_party/gerrit-queue/frontend/templates/changeset.tmpl.html +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "changeset" }} -<tr> - <td>{{ .OwnerName }}</td> - <td> - <strong>{{ .Subject }}</strong> (<a href="{{ changesetURL . }}" target="_blank">#{{ .Number }}</a>)<br /> - <small><code>{{ .CommitID }}</code></small> - </td> - <td> - <span> - {{ if .IsVerified }}<span class="badge badge-success badge-pill">+1 (CI)</span>{{ end }} - {{ if .IsCodeReviewed }}<span class="badge badge-info badge-pill">+2 (CR)</span>{{ end }} - </span> - </td> -</tr> -{{ end }} \ No newline at end of file diff --git a/third_party/gerrit-queue/frontend/templates/index.tmpl.html b/third_party/gerrit-queue/frontend/templates/index.tmpl.html deleted file mode 100644 index e04c0a349dfc..000000000000 --- a/third_party/gerrit-queue/frontend/templates/index.tmpl.html +++ /dev/null @@ -1,76 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>Gerrit Submit Queue</title> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha256-CjSoeELFOcH0/uxWu6mC/Vlrc1AARqbm/jiiImDGV3s=" crossorigin="anonymous"></script> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha256-YLGeXaapI0/5IgZopewRJcFXomhRMlYYjugPLSyNjTY=" crossorigin="anonymous" /> -</head> -<body> - <nav class="navbar sticky-top navbar-expand-sm navbar-dark bg-dark"> - <div class="container"> - <a class="navbar-brand" href="#">Gerrit Submit Queue</a> - <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> - <span class="navbar-toggler-icon"></span> - </button> - <div class="collapse navbar-collapse" id="navbarSupportedContent"> - <ul class="navbar-nav mr-auto"> - <li class="nav-item"> - <a class="nav-link" href="#region-info">Info</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="#region-wipserie">WIP Serie</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="#region-log">Log</a> - </li> - </ul> - </div> - </div> - </nav> - <div class="container"> - <h2 id="region-info">Info</h2> - <table class="table"> - <tbody> - <tr> - <th scope="row">Project Name:</th> - <td>{{ .projectName }}</td> - </tr> - <tr> - <th scope="row">Branch Name:</th> - <td>{{ .branchName }}</td> - </tr> - <tr> - <th scope="row">Currently running:</th> - <td> - {{ if .currentlyRunning }}yes{{ else }}no{{ end }} - </td> - </tr> - <tr> - <th scope="row">HEAD:</th> - <td> - {{ if .HEAD }}{{ .HEAD }}{{ else }}-{{ end }} - </td> - </tr> - </tbody> - </table> - - <h2 id="region-wipserie">WIP Serie</h2> - {{ if .wipSerie }} - {{ block "serie" .wipSerie }}{{ end }} - {{ else }} - - - {{ end }} - - <h2 id="region-log">Log</h2> - {{ range $entry := .memory.Entries }} - <div class="d-flex flex-row bg-dark {{ levelToClasses $entry.Level }} text-monospace"> - <div class="p-2"><small>{{ $entry.Timestamp.Format "2006-01-02 15:04:05 UTC"}}</small></div> - <div class="p-2 flex-grow-1"><small><strong>{{ $entry.Message }}</strong></small></div> - </div> - <div class="bg-dark {{ levelToClasses $entry.Level }} text-monospace text-break" style="padding-left: 4rem"> - <small>{{ fieldsToJSON $entry.Fields }}</small> - </div> - {{ end }} -</body> -</html> diff --git a/third_party/gerrit-queue/frontend/templates/serie.tmpl.html b/third_party/gerrit-queue/frontend/templates/serie.tmpl.html deleted file mode 100644 index 60f0c18113d7..000000000000 --- a/third_party/gerrit-queue/frontend/templates/serie.tmpl.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "serie" }} -<table class="table table-sm table-hover"> -<thead class="thead-light"> - <tr> - <th scope="col">Owner</th> - <th scope="col">Changeset</th> - <th scope="col">Flags</th> - </tr> -</thead> -<tbody> - <tr> - <td colspan="3" class="table-success">Serie with {{ len .ChangeSets }} changes</td> - </tr> - {{ range $changeset := .ChangeSets }} - {{ block "changeset" $changeset }}{{ end }} - {{ end }} -</tbody> -</table> -{{ end }} \ No newline at end of file diff --git a/third_party/gerrit-queue/gerrit/changeset.go b/third_party/gerrit-queue/gerrit/changeset.go deleted file mode 100644 index f71032a567cb..000000000000 --- a/third_party/gerrit-queue/gerrit/changeset.go +++ /dev/null @@ -1,117 +0,0 @@ -package gerrit - -import ( - "bytes" - "fmt" - - goGerrit "github.com/andygrunwald/go-gerrit" - "github.com/apex/log" -) - -// Changeset represents a single changeset -// Relationships between different changesets are described in Series -type Changeset struct { - changeInfo *goGerrit.ChangeInfo - ChangeID string - Number int - Verified int - CodeReviewed int - Autosubmit int - Submittable bool - CommitID string - ParentCommitIDs []string - OwnerName string - Subject string -} - -// MakeChangeset creates a new Changeset object out of a goGerrit.ChangeInfo object -func MakeChangeset(changeInfo *goGerrit.ChangeInfo) *Changeset { - return &Changeset{ - changeInfo: changeInfo, - ChangeID: changeInfo.ChangeID, - Number: changeInfo.Number, - Verified: labelInfoToInt(changeInfo.Labels["Verified"]), - CodeReviewed: labelInfoToInt(changeInfo.Labels["Code-Review"]), - Autosubmit: labelInfoToInt(changeInfo.Labels["Autosubmit"]), - Submittable: changeInfo.Submittable, - CommitID: changeInfo.CurrentRevision, // yes, this IS the commit ID. - ParentCommitIDs: getParentCommitIDs(changeInfo), - OwnerName: changeInfo.Owner.Name, - Subject: changeInfo.Subject, - } -} - -// IsAutosubmit returns true if the changeset is intended to be -// automatically submitted by gerrit-queue. -// -// This is determined by the Change Owner setting +1 on the -// "Autosubmit" label. -func (c *Changeset) IsAutosubmit() bool { - return c.Autosubmit == 1 -} - -// IsVerified returns true if the changeset passed CI, -// that's when somebody left the Approved (+1) on the "Verified" label -func (c *Changeset) IsVerified() bool { - return c.Verified == 1 -} - -// IsCodeReviewed returns true if the changeset passed code review, -// that's when somebody left the Recommended (+2) on the "Code-Review" label -func (c *Changeset) IsCodeReviewed() bool { - return c.CodeReviewed == 2 -} - -func (c *Changeset) String() string { - var b bytes.Buffer - b.WriteString("Changeset") - b.WriteString(fmt.Sprintf("(commitID: %.7s, author: %s, subject: %s, submittable: %v)", - c.CommitID, c.OwnerName, c.Subject, c.Submittable)) - return b.String() -} - -// FilterChangesets filters a list of Changeset by a given filter function -func FilterChangesets(changesets []*Changeset, f func(*Changeset) bool) []*Changeset { - newChangesets := make([]*Changeset, 0) - for _, changeset := range changesets { - if f(changeset) { - newChangesets = append(newChangesets, changeset) - } else { - log.WithField("changeset", changeset.String()).Debug("dropped by filter") - } - } - return newChangesets -} - -// labelInfoToInt converts a goGerrit.LabelInfo to -2…+2 int -func labelInfoToInt(labelInfo goGerrit.LabelInfo) int { - if labelInfo.Recommended.AccountID != 0 { - return 2 - } - if labelInfo.Approved.AccountID != 0 { - return 1 - } - if labelInfo.Disliked.AccountID != 0 { - return -1 - } - if labelInfo.Rejected.AccountID != 0 { - return -2 - } - return 0 -} - -// getParentCommitIDs returns the parent commit IDs of the goGerrit.ChangeInfo -// There is usually only one parent commit ID, except for merge commits. -func getParentCommitIDs(changeInfo *goGerrit.ChangeInfo) []string { - // obtain the RevisionInfo object - revisionInfo := changeInfo.Revisions[changeInfo.CurrentRevision] - - // obtain the Commit object - commit := revisionInfo.Commit - - commitIDs := make([]string, len(commit.Parents)) - for i, commit := range commit.Parents { - commitIDs[i] = commit.Commit - } - return commitIDs -} diff --git a/third_party/gerrit-queue/gerrit/client.go b/third_party/gerrit-queue/gerrit/client.go deleted file mode 100644 index 314f97281c7e..000000000000 --- a/third_party/gerrit-queue/gerrit/client.go +++ /dev/null @@ -1,220 +0,0 @@ -package gerrit - -import ( - "fmt" - - goGerrit "github.com/andygrunwald/go-gerrit" - "github.com/apex/log" - - "net/url" -) - -// passed to gerrit when retrieving changesets -var additionalFields = []string{ - "LABELS", - "CURRENT_REVISION", - "CURRENT_COMMIT", - "DETAILED_ACCOUNTS", - "SUBMITTABLE", -} - -// IClient defines the gerrit.Client interface -type IClient interface { - Refresh() error - GetHEAD() string - GetBaseURL() string - GetChangesetURL(changeset *Changeset) string - SubmitChangeset(changeset *Changeset) (*Changeset, error) - RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) - ChangesetIsRebasedOnHEAD(changeset *Changeset) bool - SerieIsRebasedOnHEAD(serie *Serie) bool - FilterSeries(filter func(s *Serie) bool) []*Serie - FindSerie(filter func(s *Serie) bool) *Serie -} - -var _ IClient = &Client{} - -// Client provides some ways to interact with a gerrit instance -type Client struct { - client *goGerrit.Client - logger *log.Logger - baseURL string - projectName string - branchName string - series []*Serie - head string -} - -// NewClient initializes a new gerrit client -func NewClient(logger *log.Logger, URL, username, password, projectName, branchName string) (*Client, error) { - urlParsed, err := url.Parse(URL) - if err != nil { - return nil, err - } - urlParsed.User = url.UserPassword(username, password) - - goGerritClient, err := goGerrit.NewClient(urlParsed.String(), nil) - if err != nil { - return nil, err - } - return &Client{ - client: goGerritClient, - baseURL: URL, - logger: logger, - projectName: projectName, - branchName: branchName, - }, nil -} - -// refreshHEAD queries the commit ID of the selected project and branch -func (c *Client) refreshHEAD() (string, error) { - branchInfo, _, err := c.client.Projects.GetBranch(c.projectName, c.branchName) - if err != nil { - return "", err - } - return branchInfo.Revision, nil -} - -// GetHEAD returns the internally stored HEAD -func (c *Client) GetHEAD() string { - return c.head -} - -// Refresh causes the client to refresh internal view of gerrit -func (c *Client) Refresh() error { - c.logger.Debug("refreshing from gerrit") - HEAD, err := c.refreshHEAD() - if err != nil { - return err - } - c.head = HEAD - - var queryString = fmt.Sprintf("status:open project:%s branch:%s", c.projectName, c.branchName) - c.logger.Debugf("fetching changesets: %s", queryString) - changesets, err := c.fetchChangesets(queryString) - if err != nil { - return err - } - - c.logger.Infof("assembling series…") - series, err := AssembleSeries(changesets, c.logger) - if err != nil { - return err - } - series = SortSeries(series) - c.series = series - return nil -} - -// fetchChangesets fetches a list of changesets matching a passed query string -func (c *Client) fetchChangesets(queryString string) (changesets []*Changeset, Error error) { - opt := &goGerrit.QueryChangeOptions{} - opt.Query = []string{ - queryString, - } - opt.AdditionalFields = additionalFields - changes, _, err := c.client.Changes.QueryChanges(opt) - if err != nil { - return nil, err - } - - changesets = make([]*Changeset, 0) - for _, change := range *changes { - changesets = append(changesets, MakeChangeset(&change)) - } - - return changesets, nil -} - -// fetchChangeset downloads an existing Changeset from gerrit, by its ID -// Gerrit's API is a bit sparse, and only returns what you explicitly ask it -// This is used to refresh an existing changeset with more data. -func (c *Client) fetchChangeset(changeID string) (*Changeset, error) { - opt := goGerrit.ChangeOptions{} - opt.AdditionalFields = []string{"LABELS", "DETAILED_ACCOUNTS"} - changeInfo, _, err := c.client.Changes.GetChange(changeID, &opt) - if err != nil { - return nil, err - } - return MakeChangeset(changeInfo), nil -} - -// SubmitChangeset submits a given changeset, and returns a changeset afterwards. -func (c *Client) SubmitChangeset(changeset *Changeset) (*Changeset, error) { - changeInfo, _, err := c.client.Changes.SubmitChange(changeset.ChangeID, &goGerrit.SubmitInput{}) - if err != nil { - return nil, err - } - c.head = changeInfo.CurrentRevision - return c.fetchChangeset(changeInfo.ChangeID) -} - -// RebaseChangeset rebases a given changeset on top of a given ref -func (c *Client) RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) { - changeInfo, _, err := c.client.Changes.RebaseChange(changeset.ChangeID, &goGerrit.RebaseInput{ - Base: ref, - }) - if err != nil { - return changeset, err - } - return c.fetchChangeset(changeInfo.ChangeID) -} - -// GetBaseURL returns the gerrit base URL -func (c *Client) GetBaseURL() string { - return c.baseURL -} - -// GetProjectName returns the configured gerrit project name -func (c *Client) GetProjectName() string { - return c.projectName -} - -// GetBranchName returns the configured gerrit branch name -func (c *Client) GetBranchName() string { - return c.branchName -} - -// GetChangesetURL returns the URL to view a given changeset -func (c *Client) GetChangesetURL(changeset *Changeset) string { - return fmt.Sprintf("%s/c/%s/+/%d", c.GetBaseURL(), c.projectName, changeset.Number) -} - -// ChangesetIsRebasedOnHEAD returns true if the changeset is rebased on the current HEAD -func (c *Client) ChangesetIsRebasedOnHEAD(changeset *Changeset) bool { - if len(changeset.ParentCommitIDs) != 1 { - return false - } - return changeset.ParentCommitIDs[0] == c.head -} - -// SerieIsRebasedOnHEAD returns true if the whole series is rebased on the current HEAD -// this is already the case if the first changeset in the series is rebased on the current HEAD -func (c *Client) SerieIsRebasedOnHEAD(serie *Serie) bool { - // an empty serie should not exist - if len(serie.ChangeSets) == 0 { - return false - } - return c.ChangesetIsRebasedOnHEAD(serie.ChangeSets[0]) -} - -// FilterSeries returns a subset of all Series, passing the given filter function -func (c *Client) FilterSeries(filter func(s *Serie) bool) []*Serie { - matchedSeries := []*Serie{} - for _, serie := range c.series { - if filter(serie) { - matchedSeries = append(matchedSeries, serie) - } - } - return matchedSeries -} - -// FindSerie returns the first serie that matches the filter, or nil if none was found -func (c *Client) FindSerie(filter func(s *Serie) bool) *Serie { - for _, serie := range c.series { - if filter(serie) { - return serie - } - } - return nil -} diff --git a/third_party/gerrit-queue/gerrit/serie.go b/third_party/gerrit-queue/gerrit/serie.go deleted file mode 100644 index 788cf46f4ea6..000000000000 --- a/third_party/gerrit-queue/gerrit/serie.go +++ /dev/null @@ -1,112 +0,0 @@ -package gerrit - -import ( - "fmt" - "strings" - - "github.com/apex/log" -) - -// Serie represents a list of successive changesets with an unbroken parent -> child relation, -// starting from the parent. -type Serie struct { - ChangeSets []*Changeset -} - -// GetParentCommitIDs returns the parent commit IDs -func (s *Serie) GetParentCommitIDs() ([]string, error) { - if len(s.ChangeSets) == 0 { - return nil, fmt.Errorf("Can't return parent on a serie with zero ChangeSets") - } - return s.ChangeSets[0].ParentCommitIDs, nil -} - -// GetLeafCommitID returns the commit id of the last commit in ChangeSets -func (s *Serie) GetLeafCommitID() (string, error) { - if len(s.ChangeSets) == 0 { - return "", fmt.Errorf("Can't return leaf on a serie with zero ChangeSets") - } - return s.ChangeSets[len(s.ChangeSets)-1].CommitID, nil -} - -// CheckIntegrity checks that the series contains a properly ordered and connected chain of commits -func (s *Serie) CheckIntegrity() error { - logger := log.WithField("serie", s) - // an empty serie is invalid - if len(s.ChangeSets) == 0 { - return fmt.Errorf("An empty serie is invalid") - } - - previousCommitID := "" - for i, changeset := range s.ChangeSets { - // we can't really check the parent of the first commit - // so skip verifying that one - logger.WithFields(log.Fields{ - "changeset": changeset.String(), - "previousCommitID": fmt.Sprintf("%.7s", previousCommitID), - }).Debug(" - verifying changeset") - - parentCommitIDs := changeset.ParentCommitIDs - if len(parentCommitIDs) == 0 { - return fmt.Errorf("Changesets without any parent are not supported") - } - // we don't check parents of the first changeset in a series - if i != 0 { - if len(parentCommitIDs) != 1 { - return fmt.Errorf("Merge commits in the middle of a series are not supported (only at the beginning)") - } - if parentCommitIDs[0] != previousCommitID { - return fmt.Errorf("changesets parent commit id doesn't match previous commit id") - } - } - // update previous commit id for the next loop iteration - previousCommitID = changeset.CommitID - } - return nil -} - -// FilterAllChangesets applies a filter function on all of the changesets in the series. -// returns true if it returns true for all changesets, false otherwise -func (s *Serie) FilterAllChangesets(f func(c *Changeset) bool) bool { - for _, changeset := range s.ChangeSets { - if f(changeset) == false { - return false - } - } - return true -} - -func (s *Serie) String() string { - var sb strings.Builder - sb.WriteString(fmt.Sprintf("Serie[%d]", len(s.ChangeSets))) - if len(s.ChangeSets) == 0 { - sb.WriteString("()\n") - return sb.String() - } - parentCommitIDs, err := s.GetParentCommitIDs() - if err == nil { - if len(parentCommitIDs) == 1 { - sb.WriteString(fmt.Sprintf("(parent: %.7s)", parentCommitIDs[0])) - } else { - sb.WriteString("(merge: ") - - for i, parentCommitID := range parentCommitIDs { - sb.WriteString(fmt.Sprintf("%.7s", parentCommitID)) - if i < len(parentCommitIDs) { - sb.WriteString(", ") - } - } - - sb.WriteString(")") - - } - } - sb.WriteString(fmt.Sprintf("(%.7s..%.7s)", - s.ChangeSets[0].CommitID, - s.ChangeSets[len(s.ChangeSets)-1].CommitID)) - return sb.String() -} - -func shortCommitID(commitID string) string { - return commitID[:6] -} diff --git a/third_party/gerrit-queue/gerrit/series.go b/third_party/gerrit-queue/gerrit/series.go deleted file mode 100644 index 295193ee9503..000000000000 --- a/third_party/gerrit-queue/gerrit/series.go +++ /dev/null @@ -1,126 +0,0 @@ -package gerrit - -import ( - "sort" - - "github.com/apex/log" -) - -// AssembleSeries consumes a list of `Changeset`, and groups them together to series -// -// We initially put every Changeset in its own Serie -// -// As we have no control over the order of the passed changesets, -// we maintain a lookup table, mapLeafToSerie, -// which allows to lookup a serie by its leaf commit id -// We concat series in a fixpoint approach -// because both appending and prepending is much more complex. -// Concatenation moves changesets of the later changeset in the previous one -// in a cleanup phase, we remove orphaned series (those without any changesets inside) -// afterwards, we do an integrity check, just to be on the safe side. -func AssembleSeries(changesets []*Changeset, logger *log.Logger) ([]*Serie, error) { - series := make([]*Serie, 0) - mapLeafToSerie := make(map[string]*Serie, 0) - - for _, changeset := range changesets { - l := logger.WithField("changeset", changeset.String()) - - l.Debug("creating initial serie") - serie := &Serie{ - ChangeSets: []*Changeset{changeset}, - } - series = append(series, serie) - mapLeafToSerie[changeset.CommitID] = serie - } - - // Combine series using a fixpoint approach, with a max iteration count. - logger.Debug("glueing together phase") - for i := 1; i < 100; i++ { - didUpdate := false - logger.Debugf("at iteration %d", i) - for j, serie := range series { - l := logger.WithFields(log.Fields{ - "i": i, - "j": j, - "serie": serie.String(), - }) - parentCommitIDs, err := serie.GetParentCommitIDs() - if err != nil { - return series, err - } - if len(parentCommitIDs) != 1 { - // We can't append merge commits to other series - l.Infof("No single parent, skipping.") - continue - } - parentCommitID := parentCommitIDs[0] - l.Debug("Looking for a predecessor.") - // if there's another serie that has this parent as a leaf, glue together - if otherSerie, ok := mapLeafToSerie[parentCommitID]; ok { - if otherSerie == serie { - continue - } - l = l.WithField("otherSerie", otherSerie) - - myLeafCommitID, err := serie.GetLeafCommitID() - if err != nil { - return series, err - } - - // append our changesets to the other serie - l.Debug("Splicing together.") - otherSerie.ChangeSets = append(otherSerie.ChangeSets, serie.ChangeSets...) - - delete(mapLeafToSerie, parentCommitID) - mapLeafToSerie[myLeafCommitID] = otherSerie - - // orphan our serie - serie.ChangeSets = []*Changeset{} - // remove the orphaned serie from the lookup table - delete(mapLeafToSerie, myLeafCommitID) - - didUpdate = true - } else { - l.Debug("Not found.") - } - } - series = removeOrphanedSeries(series) - if !didUpdate { - logger.Infof("converged after %d iterations", i) - break - } - } - - // Check integrity, just to be on the safe side. - for _, serie := range series { - l := logger.WithField("serie", serie.String()) - l.Debugf("checking integrity") - err := serie.CheckIntegrity() - if err != nil { - l.Errorf("checking integrity failed: %s", err) - } - } - return series, nil -} - -// removeOrphanedSeries removes all empty series (that contain zero changesets) -func removeOrphanedSeries(series []*Serie) []*Serie { - newSeries := []*Serie{} - for _, serie := range series { - if len(serie.ChangeSets) != 0 { - newSeries = append(newSeries, serie) - } - } - return newSeries -} - -// SortSeries sorts a list of series by the number of changesets in each serie, descending -func SortSeries(series []*Serie) []*Serie { - newSeries := make([]*Serie, len(series)) - copy(newSeries, series) - sort.Slice(newSeries, func(i, j int) bool { - // the weight depends on the amount of changesets series changeset size - return len(series[i].ChangeSets) > len(series[j].ChangeSets) - }) - return newSeries -} diff --git a/third_party/gerrit-queue/go.mod b/third_party/gerrit-queue/go.mod deleted file mode 100644 index 3929f8cf657e..000000000000 --- a/third_party/gerrit-queue/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/tweag/gerrit-queue - -go 1.16 - -require ( - github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 - github.com/apex/log v1.1.1 - github.com/google/go-querystring v1.0.0 // indirect - github.com/urfave/cli v1.22.1 -) diff --git a/third_party/gerrit-queue/go.sum b/third_party/gerrit-queue/go.sum deleted file mode 100644 index d11545a6c9d5..000000000000 --- a/third_party/gerrit-queue/go.sum +++ /dev/null @@ -1,69 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 h1:9PvNa6zH6gOW4VVfbAx5rjDLpxunG+RSaXQB+8TEv4w= -github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk= -github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA= -github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/third_party/gerrit-queue/main.go b/third_party/gerrit-queue/main.go deleted file mode 100644 index eaa792c95828..000000000000 --- a/third_party/gerrit-queue/main.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "os" - "time" - - "net/http" - - "github.com/tweag/gerrit-queue/frontend" - "github.com/tweag/gerrit-queue/gerrit" - "github.com/tweag/gerrit-queue/misc" - "github.com/tweag/gerrit-queue/submitqueue" - - "github.com/urfave/cli" - - "github.com/apex/log" - "github.com/apex/log/handlers/multi" - "github.com/apex/log/handlers/text" -) - -func main() { - var URL, username, password, projectName, branchName string - var fetchOnly bool - var triggerInterval int - - app := cli.NewApp() - app.Name = "gerrit-queue" - - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "url", - Usage: "URL to the gerrit instance", - EnvVar: "GERRIT_URL", - Destination: &URL, - Required: true, - }, - cli.StringFlag{ - Name: "username", - Usage: "Username to use to login to gerrit", - EnvVar: "GERRIT_USERNAME", - Destination: &username, - Required: true, - }, - cli.StringFlag{ - Name: "password", - Usage: "Password to use to login to gerrit", - EnvVar: "GERRIT_PASSWORD", - Destination: &password, - Required: true, - }, - cli.StringFlag{ - Name: "project", - Usage: "Gerrit project name to run the submit queue for", - EnvVar: "GERRIT_PROJECT", - Destination: &projectName, - Required: true, - }, - cli.StringFlag{ - Name: "branch", - Usage: "Destination branch", - EnvVar: "GERRIT_BRANCH", - Destination: &branchName, - Value: "master", - }, - cli.IntFlag{ - Name: "trigger-interval", - Usage: "How often we should trigger ourselves (interval in seconds)", - EnvVar: "SUBMIT_QUEUE_TRIGGER_INTERVAL", - Destination: &triggerInterval, - Value: 600, - }, - cli.BoolFlag{ - Name: "fetch-only", - Usage: "Only fetch changes and assemble queue, but don't actually write", - EnvVar: "SUBMIT_QUEUE_FETCH_ONLY", - Destination: &fetchOnly, - }, - } - - rotatingLogHandler := misc.NewRotatingLogHandler(10000) - l := &log.Logger{ - Handler: multi.New( - text.New(os.Stderr), - rotatingLogHandler, - ), - Level: log.DebugLevel, - } - - app.Action = func(c *cli.Context) error { - gerrit, err := gerrit.NewClient(l, URL, username, password, projectName, branchName) - if err != nil { - return err - } - log.Infof("Successfully connected to gerrit at %s", URL) - - runner := submitqueue.NewRunner(l, gerrit) - - handler := frontend.MakeFrontend(rotatingLogHandler, gerrit, runner) - - // fetch only on first run - err = runner.Trigger(fetchOnly) - if err != nil { - log.Error(err.Error()) - } - - // ticker - go func() { - for { - time.Sleep(time.Duration(triggerInterval) * time.Second) - err = runner.Trigger(fetchOnly) - if err != nil { - log.Error(err.Error()) - } - } - }() - - server := http.Server{ - Addr: ":8080", - Handler: handler, - } - - server.ListenAndServe() - if err != nil { - log.Fatalf(err.Error()) - } - - return nil - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err.Error()) - } - - // TODOS: - // - handle event log, either by accepting webhooks, or by streaming events? -} diff --git a/third_party/gerrit-queue/misc/rotatingloghandler.go b/third_party/gerrit-queue/misc/rotatingloghandler.go deleted file mode 100644 index 3d4c5f3a837a..000000000000 --- a/third_party/gerrit-queue/misc/rotatingloghandler.go +++ /dev/null @@ -1,34 +0,0 @@ -package misc - -import ( - "sync" - - "github.com/apex/log" -) - -// RotatingLogHandler implementation. -type RotatingLogHandler struct { - mu sync.Mutex - Entries []*log.Entry - maxEntries int -} - -// NewRotatingLogHandler creates a new rotating log handler -func NewRotatingLogHandler(maxEntries int) *RotatingLogHandler { - return &RotatingLogHandler{ - maxEntries: maxEntries, - } -} - -// HandleLog implements log.Handler. -func (h *RotatingLogHandler) HandleLog(e *log.Entry) error { - h.mu.Lock() - defer h.mu.Unlock() - // drop tail if we have more entries than maxEntries - if len(h.Entries) > h.maxEntries { - h.Entries = append([]*log.Entry{e}, h.Entries[:(h.maxEntries-2)]...) - } else { - h.Entries = append([]*log.Entry{e}, h.Entries...) - } - return nil -} diff --git a/third_party/gerrit-queue/submitqueue/runner.go b/third_party/gerrit-queue/submitqueue/runner.go deleted file mode 100644 index 0b4cbcd8dd7c..000000000000 --- a/third_party/gerrit-queue/submitqueue/runner.go +++ /dev/null @@ -1,220 +0,0 @@ -package submitqueue - -import ( - "fmt" - "sync" - - "github.com/apex/log" - - "github.com/tweag/gerrit-queue/gerrit" -) - -// Runner is a struct existing across the lifetime of a single run of the submit queue -// it contains a mutex to avoid being run multiple times. -// In fact, it even cancels runs while another one is still in progress. -// It contains a Gerrit object facilitating access, a log object, the configured submit queue tag -// and a `wipSerie` (only populated if waiting for a rebase) -type Runner struct { - mut sync.Mutex - currentlyRunning bool - wipSerie *gerrit.Serie - logger *log.Logger - gerrit *gerrit.Client -} - -// NewRunner creates a new Runner struct -func NewRunner(logger *log.Logger, gerrit *gerrit.Client) *Runner { - return &Runner{ - logger: logger, - gerrit: gerrit, - } -} - -// isAutoSubmittable determines if something could be autosubmitted, potentially requiring a rebase -// for this, it needs to: -// * have the "Autosubmit" label set to +1 -// * have gerrit's 'submittable' field set to true -// it doesn't check if the series is rebased on HEAD -func (r *Runner) isAutoSubmittable(s *gerrit.Serie) bool { - for _, c := range s.ChangeSets { - if c.Submittable != true || !c.IsAutosubmit() { - return false - } - } - return true -} - -// IsCurrentlyRunning returns true if the runner is currently running -func (r *Runner) IsCurrentlyRunning() bool { - return r.currentlyRunning -} - -// GetWIPSerie returns the current wipSerie, if any, nil otherwiese -// Acquires a lock, so check with IsCurrentlyRunning first -func (r *Runner) GetWIPSerie() *gerrit.Serie { - r.mut.Lock() - defer func() { - r.mut.Unlock() - }() - return r.wipSerie -} - -// Trigger gets triggered periodically -func (r *Runner) Trigger(fetchOnly bool) error { - // TODO: If CI fails, remove the auto-submit labels => rules.pl - // Only one trigger can run at the same time - r.mut.Lock() - if r.currentlyRunning { - return fmt.Errorf("Already running, skipping") - } - r.currentlyRunning = true - r.mut.Unlock() - defer func() { - r.mut.Lock() - r.currentlyRunning = false - r.mut.Unlock() - }() - - // Prepare the work by creating a local cache of gerrit state - err := r.gerrit.Refresh() - if err != nil { - return err - } - - // early return if we only want to fetch - if fetchOnly { - return nil - } - - if r.wipSerie != nil { - // refresh wipSerie with how it looks like in gerrit now - wipSerie := r.gerrit.FindSerie(func(s *gerrit.Serie) bool { - // the new wipSerie needs to have the same number of changesets - if len(r.wipSerie.ChangeSets) != len(s.ChangeSets) { - return false - } - // … and the same ChangeIDs. - for idx, c := range s.ChangeSets { - if r.wipSerie.ChangeSets[idx].ChangeID != c.ChangeID { - return false - } - } - return true - }) - if wipSerie == nil { - r.logger.WithField("wipSerie", r.wipSerie).Warn("wipSerie has disappeared") - r.wipSerie = nil - } else { - r.wipSerie = wipSerie - } - } - - for { - // initialize logger - r.logger.Info("Running") - if r.wipSerie != nil { - // if we have a wipSerie - l := r.logger.WithField("wipSerie", r.wipSerie) - l.Info("Checking wipSerie") - - // discard wipSerie not rebased on HEAD - // we rebase them at the end of the loop, so this means master advanced without going through the submit queue - if !r.gerrit.SerieIsRebasedOnHEAD(r.wipSerie) { - l.Warnf("HEAD has moved to %v while still waiting for wipSerie, discarding it", r.gerrit.GetHEAD()) - r.wipSerie = nil - continue - } - - // we now need to check CI feedback: - // wipSerie might have failed CI in the meantime - for _, c := range r.wipSerie.ChangeSets { - if c == nil { - l.Error("BUG: changeset is nil") - continue - } - if c.Verified < 0 { - l.WithField("failingChangeset", c).Warnf("wipSerie failed CI in the meantime, discarding.") - r.wipSerie = nil - continue - } - } - - // it might still be waiting for CI - for _, c := range r.wipSerie.ChangeSets { - if c == nil { - l.Error("BUG: changeset is nil") - continue - } - if c.Verified == 0 { - l.WithField("pendingChangeset", c).Warnf("still waiting for CI feedback in wipSerie, going back to sleep.") - // break the loop, take a look at it at the next trigger. - return nil - } - } - - // it might be autosubmittable - if r.isAutoSubmittable(r.wipSerie) { - l.Infof("submitting wipSerie") - // if the WIP changeset is ready (auto submittable and rebased on HEAD), submit - for _, changeset := range r.wipSerie.ChangeSets { - _, err := r.gerrit.SubmitChangeset(changeset) - if err != nil { - l.WithField("changeset", changeset).Error("error submitting changeset") - r.wipSerie = nil - return err - } - } - r.wipSerie = nil - } else { - l.Error("BUG: wipSerie is not autosubmittable") - r.wipSerie = nil - } - } - - r.logger.Info("Looking for series ready to submit") - // Find serie, that: - // * has the auto-submit label - // * has +2 review - // * has +1 CI - // * is rebased on master - serie := r.gerrit.FindSerie(func(s *gerrit.Serie) bool { - return r.isAutoSubmittable(s) && s.ChangeSets[0].ParentCommitIDs[0] == r.gerrit.GetHEAD() - }) - if serie != nil { - r.logger.WithField("serie", serie).Info("Found serie to submit without necessary rebase") - r.wipSerie = serie - continue - } - - // Find serie, that: - // * has the auto-submit label - // * has +2 review - // * has +1 CI - // * is NOT rebased on master - serie = r.gerrit.FindSerie(r.isAutoSubmittable) - if serie == nil { - r.logger.Info("no more submittable series found, going back to sleep.") - break - } - - l := r.logger.WithField("serie", serie) - l.Info("found serie, which needs a rebase") - // TODO: move into Client.RebaseSeries function - head := r.gerrit.GetHEAD() - for _, changeset := range serie.ChangeSets { - changeset, err := r.gerrit.RebaseChangeset(changeset, head) - if err != nil { - l.Error(err.Error()) - return err - } - head = changeset.CommitID - } - // we don't need to care about updating the rebased changesets or getting the updated HEAD, - // as we'll refetch it on the beginning of the next trigger anyways - r.wipSerie = serie - break - } - - r.logger.Info("Run complete") - return nil -} diff --git a/third_party/overlays/tvl.nix b/third_party/overlays/tvl.nix index 4dfd72eceb6f..909344cb5e96 100644 --- a/third_party/overlays/tvl.nix +++ b/third_party/overlays/tvl.nix @@ -138,4 +138,23 @@ depot.nix.readTree.drvTargets { ./patches/evans-add-support-for-unix-domain-sockets.patch ]; }); + + # Package gerrit-queue, which is not in nixpkgs yet + gerrit-queue = super.buildGoModule { + pname = "gerrit-queue"; + version = "unstable-2023-10-20"; + vendorHash = "sha256-+Ig4D46NphzpWKXO23Haea9EqVtpda8v9zLPJkbe3bQ="; + src = super.fetchFromGitHub { + owner = "flokli"; + repo = "gerrit-queue"; + rev = "0186dbde15c9b11dc17b422feb74c842f6fa605a"; + hash = "sha256-zXB5vre/Vr7UOyeMnf2RCtMKm+v5RENH7kGPr/2o7mI="; + }; + + meta = with lib; { + description = "Gerrit submit bot"; + homepage = "https://github.com/tweag/gerrit-queue"; + license = licenses.asl20; + }; + }; } |