aboutsummaryrefslogtreecommitdiffstats
path: root/plugin_bots/plugin_template/plugin_template.js
blob: 05cb877fdc5eca735bb8c28a3b200c03386e6527 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* eslint-disable no-unused-vars */
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

exports.id = "plugin_template";

// Cherry-pick bot requires using the "require()" function to load any modules,
// rather than the "import" statement.
const config = require("./config.json");

// Non-instanced modules can be loaded directly.
const gerritTools = require("../../gerritRESTTools");


// Set default values with the config file, but prefer environment variable.
function envOrConfig(ID) {
  return process.env[ID] || config[ID];
}

// If you need to use a custom username/password for gerrit, you can set them
// here. Otherwise, the default cherry-pick-bot credentials will be used.
const customAuth = {
  username: envOrConfig("MY_BOT_USER"),
  password: envOrConfig("MY_BOT_PASS")
}

// This defines the class that will be instantiated by the notifier.
// The notifier will pass itself as the only argument to the constructor.
// This allows the plugin to access the notifier's properties and methods.
class plugin_template {
  constructor(notifier) {
    this.notifier = notifier; // /notifier.js
    this.logger = notifier.logger; // /logger.js
    this.retryProcessor = notifier.retryProcessor; // /retryProcessor.js
    this.requestProcessor = notifier.requestProcessor; // /requestProcessor.js

    // This line binds our local processPatch function to the
    // the `this` context of this class. This is necessary because we will
    // be passing this function to the server as a callback, and we want
    // to be able to access the class's properties and methods from within
    // the callback.
    this.processPatch = this.processPatch.bind(this);

    // This call to registerCustomListener binds our local processPatch
    // function to the "integration_monitor_process_patch" event, emitted by
    // 'server'. When the event is emitted, our function will be called with
    //  any arguments passed by the "emit()" call.
    notifier.registerCustomListener(notifier.server, "integration_monitor_process_patch",
                                    this.processPatch);

    // This call to registerCustomEvent tells the server to call the inline
    // callback when it receives a "patchset-created" event. The callback will
    // always be called with the request object (incoming change data from
    // codereview as its only argument).
    notifier.server.registerCustomEvent("integration_monitor_entryPoint", "patchset-created",
      (req) => { // Use an arrow function to preserve the "this" context.

        // Do some trivial checks to see if we should do anything.
        if (req.change.status == "MERGED")
          return;  // The CI created a new patchset upon change merge. Don't do anything.

        // Check to see if a real person uploaded the last patchset.
        let uploader = req.uploader.email;
        let pickbotIsUploader = (uploader == "cherrypick_bot@qt-project.org");
        if (req.change.owner.email != "cherrypick_bot@qt-project.org" && !pickbotIsUploader) {
          // A real user is the uploader.
          // Emit the internal event we registered above. This will call our
          // processPatch function with the req and uploader arguments.
          notifier.server.emit("integration_monitor_process_patch", req, uploader);
        }
      }
    );
  }

  // This is the callback function that will be called when the
  // "integration_monitor_process_patch" event is emitted in the event handling above.
  processPatch(req, uploader) {
    this.logger.log(
      `Received patchset-created by ${uploader} for cherry-picked change in ${req.change.project}`,
      "info", req.uuid
    );

    // Patchset-created does not include a full change ID. Assemble one. This is needed
    // for avoiding conflicts in gerrit where the same change ID exists on multiple branches.
    req.fullChangeID = encodeURIComponent(`${req.change.project}~${req.change.branch}~${req.change.id}`);
    req.change.fullChangeID = req.fullChangeID;

    // Dip into gerritTools to get some change details.
    gerritTools.getChangeReviewers(req.uuid, req.change.fullChangeID, customAuth,
      (success, reviewers) => {
        if (success)
          this.logger.log(`Reviewers: ${reviewers}`, "info", req.uuid);
        else
          this.logger.log(`Failed to get reviewers for ${req.change.fullChangeID}`,
            "error", req.uuid);
      }
    );
  }
}

module.exports = plugin_template;