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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
// Copyright (C) 2022 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
const Axios = require("axios");
const axiosRateLimit = require("axios-rate-limit")
const { setupCache, buildMemoryStorage,
defaultKeyGenerator, defaultHeaderInterpreter } = require('axios-cache-interceptor');
const moment = require("moment");
const ConfigIniParser = require("config-ini-parser").ConfigIniParser;
const gerritTools = require("../../gerritRESTTools");
const Logger = require("../../logger");
const logger = new Logger();
const config = require("./config.json");
const res = require("express/lib/response");
function envOrConfig(ID) {
return process.env[ID] || config[ID];
}
let gerritURL = envOrConfig("JIRA_GERRIT_URL");
let gerritPort = envOrConfig("GERRIT_PORT") || 443;
let gerritResolvedURL = /^https?:\/\//g.test(gerritURL)
? gerritURL
: `${gerritPort == 80 ? "http" : "https"}://${gerritURL}`;
gerritResolvedURL += gerritPort != 80 && gerritPort != 443 ? ":" + gerritPort : "";
let gerritAuth = {
username: envOrConfig("JIRA_GERRIT_USER"),
password: envOrConfig("JIRA_GERRIT_PASSWORD")
};
// Boilerplate from https://axios-cache-interceptor.js.org/#/pages/configuration
const axios = setupCache(
// axios instance
axiosRateLimit(Axios.create(), { maxRPS: 20 }),
// All options with their default values
{
// The storage to save the cache data. There are more available by default.
//
// https://axios-cache-interceptor.js.org/#/pages/storages
storage: buildMemoryStorage(),
// The mechanism to generate a unique key for each request.
//
// https://axios-cache-interceptor.js.org/#/pages/request-id
generateKey: defaultKeyGenerator,
// The mechanism to interpret headers (when cache.interpretHeader is true).
//
// https://axios-cache-interceptor.js.org/#/pages/global-configuration?id=headerinterpreter
headerInterpreter: defaultHeaderInterpreter,
// The function that will receive debug information.
// NOTE: For this to work, you need to enable development mode.
//
// https://axios-cache-interceptor.js.org/#/pages/development-mode
// https://axios-cache-interceptor.js.org/#/pages/global-configuration?id=debug
debug: undefined
}
);
// Return a promise for validateBranch, which normally requires a callback.
const promisedVerifyBranch = async function (uuid, project, branch, branchesCache) { //, expandSearch) {
return new Promise((resolve) => {
// 5.0 branch predates gerrit, and some projects did not start with 6.0
// However, it is expected that all projects with 5.x or 6.x branches should
// be based on a X.0 release.
if (branch == "5.0" || branch == "6.0") {
resolve(true);
return;
}
try {
// Cached branches will resolve with a Moment of the branch creation.
const branchDate = branchesCache[project][branch];
if (branchDate) {
resolve(branchDate);
return;
}
}
catch (e) {} // Expected if we don't have the branch cached.
// If we don't have the branch cached, we need to fetch it.
gerritTools.validateBranch(uuid, project, branch, gerritAuth,
function(success, data) {
if (success) {
resolve(data);
} else {
resolve(false);
}
}
);
});
}
// Get the inheritance of a repo
async function getAccessInheritanceRepo(uuid, repo) {
let data;
const url = `${gerritResolvedURL}/a/projects/${encodeURIComponent(repo)}/access`;
try {
logger.log(url, "verbose", uuid);
const resp = await axios.get(url, { auth: gerritAuth })
data = JSON.parse(gerritTools.trimResponse(resp.data));
} catch(e) {
logger.log(e, "error", uuid);
return false; // safely catch if we requested an invalid repo
}
if (data.inherits_from && data.inherits_from.id) {
return data.inherits_from.id;
} else {
// No further inheritance exists, this is a top-level repo.
return false;
}
}
async function repoUsesCherryPicking(uuid, repo) {
let data;
const url = `${gerritResolvedURL}/a/projects/${encodeURIComponent(repo)}`
+ `/branches/refs%2Fmeta%2Fconfig/files/webhooks.config/content`;
logger.log(url, "debug", uuid);
const resp = await axios.get(url, { auth: gerritAuth })
.catch(error => {
if (error.response && error.response.status == 404)
return error; // expected, but we have to catch the error safely.
else
throw(error); // Something else failed and should fall through.
});
if (resp.status == 200) {
// Can only call parse once on the object, so create it here instead of globally.
const iniParser = new ConfigIniParser();
data = iniParser.parse(Buffer.from(resp.data, 'base64').toString('ascii'));
if (data.isHaveSection('remote "qt-cherry-pick-bot"'))
return true;
}
// Not cherry-picking at this repo level. Check parent repo.
const parentRepo = await getAccessInheritanceRepo(uuid, repo);
if (parentRepo)
return repoUsesCherryPicking(uuid, parentRepo)
else
return false; // Could not find any parent repo with webhooks.yaml
}
// Check to see what branch a commit appears in.
// Useful when a change in included in a branching event such as 6.4 -> 6.4.2
async function getCommitInBranches(uuid, repo, commit) {
let data;
const url = `${gerritResolvedURL}/a/projects/${encodeURIComponent(repo)}/commits/${commit}/in`;
try {
logger.log(url, "verbose", uuid);
const resp = await axios.get(url, { auth: gerritAuth });
data = JSON.parse(gerritTools.trimResponse(resp.data));
return data.branches;
} catch(e) {
logger.log(e, "error", uuid);
return []; // Default to thinking this is a Qt/ based change?
}
}
// Query $JIRA_BACKLOG_DAYS worth of merged issues.
async function getChangesWithFooter(uuid, queryOverride) {
const days = Number(process.env.JIRA_BACKLOG_DAYS);
if ((!days || days < 1) && !queryOverride)
return [];
const age = moment().subtract(days, 'days').format('YYYY-MM-DD');
const querystring = queryOverride || `(message:"\nFixes: .+$" OR message:"\nTask-Number: .+$")`
+ `+AND+status:merged+AND+mergedafter:${age}&o=CURRENT_REVISION&o=CURRENT_COMMIT&no-limit`;
// + `+AND+status:merged+AND+mergedafter:2023-07-06+mergedbefore:2023-07-08&o=CURRENT_REVISION&o=CURRENT_COMMIT`;
// + `+AND+commit:7d426b6226aa052f1dbbdc08a0b67dae8ba115e0&o=CURRENT_REVISION&o=CURRENT_COMMIT`
const url = `${gerritResolvedURL}/a/changes/?q=${querystring}`;
try {
logger.log(url, 'debug', uuid);
const resp = await axios.get(url, { auth: gerritAuth });
let data = JSON.parse(gerritTools.trimResponse(resp.data));
return data;
} catch(e) {
console.log(e);
return [];
}
}
module.exports = { promisedVerifyBranch, repoUsesCherryPicking, getCommitInBranches,
getChangesWithFooter };
|