about summary refs log tree commit diff
path: root/third_party
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-10-20T12·18+0100
committerflokli <flokli@flokli.de>2023-10-30T09·07+0000
commit9a1e5cf4c731c082032285e9b02e453fd0d11483 (patch)
tree7c6a265f3ee80146275a621154196d1f31fa170a /third_party
parent2513ddd2b7559628a0135ec0c8e4e8ea4444af8d (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')
-rwxr-xr-xthird_party/gerrit-queue/.buildkite/build.sh4
-rw-r--r--third_party/gerrit-queue/.buildkite/pipeline.yml13
-rw-r--r--third_party/gerrit-queue/.gitignore4
-rw-r--r--third_party/gerrit-queue/LICENSE201
-rw-r--r--third_party/gerrit-queue/README.md80
-rw-r--r--third_party/gerrit-queue/default.nix14
-rw-r--r--third_party/gerrit-queue/frontend/frontend.go113
-rw-r--r--third_party/gerrit-queue/frontend/templates/changeset.tmpl.html15
-rw-r--r--third_party/gerrit-queue/frontend/templates/index.tmpl.html76
-rw-r--r--third_party/gerrit-queue/frontend/templates/serie.tmpl.html19
-rw-r--r--third_party/gerrit-queue/gerrit/changeset.go117
-rw-r--r--third_party/gerrit-queue/gerrit/client.go220
-rw-r--r--third_party/gerrit-queue/gerrit/serie.go112
-rw-r--r--third_party/gerrit-queue/gerrit/series.go126
-rw-r--r--third_party/gerrit-queue/go.mod10
-rw-r--r--third_party/gerrit-queue/go.sum69
-rw-r--r--third_party/gerrit-queue/main.go137
-rw-r--r--third_party/gerrit-queue/misc/rotatingloghandler.go34
-rw-r--r--third_party/gerrit-queue/submitqueue/runner.go220
-rw-r--r--third_party/overlays/tvl.nix19
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 0a218c817e..0000000000
--- 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 0885cb9694..0000000000
--- 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 f2ec770e3c..0000000000
--- 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 261eeb9e9f..0000000000
--- 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 9ffb81b8d2..0000000000
--- 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 d3988f59d1..0000000000
--- 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 2cc65423f0..0000000000
--- 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 5d3997885c..0000000000
--- 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 e04c0a349d..0000000000
--- 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 60f0c18113..0000000000
--- 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 f71032a567..0000000000
--- 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 314f97281c..0000000000
--- 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 788cf46f4e..0000000000
--- 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 295193ee95..0000000000
--- 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 3929f8cf65..0000000000
--- 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 d11545a6c9..0000000000
--- 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 eaa792c958..0000000000
--- 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 3d4c5f3a83..0000000000
--- 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 0b4cbcd8dd..0000000000
--- 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 4dfd72eceb..909344cb5e 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;
+    };
+  };
 }