renovate 44.84.0 → 44.85.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/presets/forgejo/index.js +3 -2
- package/dist/config/presets/forgejo/index.js.map +1 -1
- package/dist/config/presets/gitea/index.js +10 -3
- package/dist/config/presets/gitea/index.js.map +1 -1
- package/dist/constants/error-messages.d.ts +3 -2
- package/dist/constants/error-messages.js +3 -1
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/modules/platform/api.js +6 -6
- package/dist/modules/platform/forgejo/index.js +21 -587
- package/dist/modules/platform/forgejo/index.js.map +1 -1
- package/dist/modules/platform/gitea/gitea-helper.js +90 -74
- package/dist/modules/platform/gitea/gitea-helper.js.map +1 -1
- package/dist/modules/platform/gitea/index.js +561 -529
- package/dist/modules/platform/gitea/index.js.map +1 -1
- package/dist/modules/platform/gitea/labels.js +42 -0
- package/dist/modules/platform/gitea/labels.js.map +1 -0
- package/dist/modules/platform/gitea/pr-cache.js +66 -28
- package/dist/modules/platform/gitea/pr-cache.js.map +1 -1
- package/dist/modules/platform/gitea/schema.js +141 -5
- package/dist/modules/platform/gitea/schema.js.map +1 -1
- package/dist/modules/platform/gitea/utils.js +10 -8
- package/dist/modules/platform/gitea/utils.js.map +1 -1
- package/dist/modules/platform/gitlab/index.js +11 -3
- package/dist/modules/platform/gitlab/index.js.map +1 -1
- package/dist/util/cache/repository/types.d.ts +13 -8
- package/dist/util/http/forgejo.js +6 -35
- package/dist/util/http/forgejo.js.map +1 -1
- package/dist/workers/repository/error.js +5 -0
- package/dist/workers/repository/error.js.map +1 -1
- package/dist/workers/repository/process/lookup/index.js +2 -2
- package/dist/workers/repository/process/lookup/index.js.map +1 -1
- package/dist/workers/repository/update/pr/changelog/forgejo/index.js +1 -1
- package/dist/workers/repository/update/pr/changelog/forgejo/index.js.map +1 -1
- package/dist/workers/repository/update/pr/changelog/gitea/index.js +3 -3
- package/dist/workers/repository/update/pr/changelog/gitea/index.js.map +1 -1
- package/package.json +1 -1
- package/renovate-schema.json +2 -2
- package/dist/modules/platform/forgejo/forgejo-helper.js +0 -235
- package/dist/modules/platform/forgejo/forgejo-helper.js.map +0 -1
- package/dist/modules/platform/forgejo/pr-cache.js +0 -114
- package/dist/modules/platform/forgejo/pr-cache.js.map +0 -1
- package/dist/modules/platform/forgejo/schema.js +0 -149
- package/dist/modules/platform/forgejo/schema.js.map +0 -1
- package/dist/modules/platform/forgejo/utils.js +0 -115
- package/dist/modules/platform/forgejo/utils.js.map +0 -1
|
@@ -1,22 +1,7 @@
|
|
|
1
1
|
import { __exportAll } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { REPOSITORY_ACCESS_FORBIDDEN, REPOSITORY_ARCHIVED, REPOSITORY_BLOCKED, REPOSITORY_CHANGED, REPOSITORY_EMPTY, REPOSITORY_MIRRORED } from "../../../constants/error-messages.js";
|
|
3
|
-
import { getEnv } from "../../../util/env.js";
|
|
4
|
-
import { GlobalConfig } from "../../../config/global.js";
|
|
5
|
-
import { coerceArray, deduplicateArray } from "../../../util/array.js";
|
|
6
|
-
import { sanitize } from "../../../util/sanitize.js";
|
|
7
2
|
import { logger } from "../../../logger/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { map } from "../../../util/promises.js";
|
|
11
|
-
import { getBranchCommit, initRepo as initRepo$1 } from "../../../util/git/index.js";
|
|
12
|
-
import { setBaseUrl } from "../../../util/http/forgejo.js";
|
|
13
|
-
import { getPrBodyStruct, hashBody } from "../pr-body.js";
|
|
14
|
-
import { repoFingerprint } from "../util.js";
|
|
15
|
-
import { smartTruncate } from "../utils/pr-body.js";
|
|
16
|
-
import { DRAFT_PREFIX, getMergeMethod, getRepoUrl, isAllowed, smartLinks, toRenovatePR, trimTrailingApiPath, usableRepo } from "./utils.js";
|
|
17
|
-
import { closeIssue, createComment, createCommitStatus, createIssue, createPR, deleteComment, forgejoHttp, forgejoToRenovateStatusMapping, getCombinedCommitStatus, getComments, getCurrentUser, getIssue as getIssue$1, getOrgLabels, getPR, getPRByBranch, getRepo, getRepoContents, getRepoLabels, getVersion, isOrg, mergePR, orgListRepos, renovateToForgejoStatusMapping, requestPrReviewers, searchIssues, searchRepos, unassignLabel, updateComment, updateIssue, updateIssueLabels, updatePR } from "./forgejo-helper.js";
|
|
18
|
-
import { ForgejoPrCache } from "./pr-cache.js";
|
|
19
|
-
import { isNumber, isString } from "@sindresorhus/is";
|
|
3
|
+
import { ForgejoHttp, setBaseUrl } from "../../../util/http/forgejo.js";
|
|
4
|
+
import { createPlatform } from "../gitea/index.js";
|
|
20
5
|
import semver from "semver";
|
|
21
6
|
//#region lib/modules/platform/forgejo/index.ts
|
|
22
7
|
var forgejo_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -30,6 +15,7 @@ var forgejo_exports = /* @__PURE__ */ __exportAll({
|
|
|
30
15
|
ensureIssueClosing: () => ensureIssueClosing,
|
|
31
16
|
findIssue: () => findIssue,
|
|
32
17
|
findPr: () => findPr,
|
|
18
|
+
forgejoHttp: () => forgejoHttp,
|
|
33
19
|
getBranchPr: () => getBranchPr,
|
|
34
20
|
getBranchStatus: () => getBranchStatus,
|
|
35
21
|
getBranchStatusCheck: () => getBranchStatusCheck,
|
|
@@ -51,576 +37,24 @@ var forgejo_exports = /* @__PURE__ */ __exportAll({
|
|
|
51
37
|
updatePr: () => updatePr
|
|
52
38
|
});
|
|
53
39
|
const id = "forgejo";
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
function toRenovateIssue(data) {
|
|
72
|
-
return {
|
|
73
|
-
number: data.number,
|
|
74
|
-
state: data.state,
|
|
75
|
-
title: data.title,
|
|
76
|
-
body: data.body
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
function matchesState(actual, expected) {
|
|
80
|
-
if (expected === "all") return true;
|
|
81
|
-
if (expected.startsWith("!")) return actual !== expected.substring(1);
|
|
82
|
-
return actual === expected;
|
|
83
|
-
}
|
|
84
|
-
function findCommentByTopic(comments, topic) {
|
|
85
|
-
return comments.find((c) => c.body.startsWith(`### ${topic}\n\n`)) ?? null;
|
|
86
|
-
}
|
|
87
|
-
function findCommentByContent(comments, content) {
|
|
88
|
-
return comments.find((c) => c.body.trim() === content) ?? null;
|
|
89
|
-
}
|
|
90
|
-
function getLabelList() {
|
|
91
|
-
if (config.labelList === null) {
|
|
92
|
-
const repoLabels = getRepoLabels(config.repository, { memCache: false }).then((labels) => {
|
|
93
|
-
logger.debug(`Retrieved ${labels.length} repo labels`);
|
|
94
|
-
return labels;
|
|
95
|
-
});
|
|
96
|
-
const orgLabels = config.isOrgRepo ? getOrgLabels(config.orgName, { memCache: false }).then((labels) => {
|
|
97
|
-
logger.debug(`Retrieved ${labels.length} org labels`);
|
|
98
|
-
return labels;
|
|
99
|
-
}).catch((err) => {
|
|
100
|
-
logger.debug({ err }, `Unable to fetch organization labels`);
|
|
101
|
-
return [];
|
|
102
|
-
}) : Promise.resolve([]);
|
|
103
|
-
config.labelList = Promise.all([repoLabels, orgLabels]).then((labels) => [].concat(...labels));
|
|
104
|
-
}
|
|
105
|
-
return config.labelList;
|
|
106
|
-
}
|
|
107
|
-
async function lookupLabelByName(name) {
|
|
108
|
-
logger.debug(`lookupLabelByName(${name})`);
|
|
109
|
-
return (await getLabelList()).find((l) => l.name === name)?.id ?? null;
|
|
110
|
-
}
|
|
111
|
-
async function fetchRepositories({ topic, sort, order }) {
|
|
112
|
-
return (await searchRepos({
|
|
113
|
-
uid: botUserID,
|
|
114
|
-
archived: false,
|
|
115
|
-
...topic && {
|
|
116
|
-
topic: true,
|
|
117
|
-
q: topic
|
|
118
|
-
},
|
|
119
|
-
...sort && { sort },
|
|
120
|
-
...order && { order }
|
|
121
|
-
})).filter(usableRepo).map((r) => r.full_name);
|
|
122
|
-
}
|
|
123
|
-
const platform = {
|
|
124
|
-
async initPlatform({ endpoint, token }) {
|
|
125
|
-
if (!token) throw new Error("Init: You must configure a Forgejo personal access token");
|
|
126
|
-
if (endpoint) {
|
|
127
|
-
let baseEndpoint = trimTrailingApiPath(endpoint);
|
|
128
|
-
baseEndpoint = ensureTrailingSlash(baseEndpoint);
|
|
129
|
-
defaults.endpoint = baseEndpoint;
|
|
130
|
-
} else logger.debug(`Using default Forgejo endpoint: ${defaults.endpoint}`);
|
|
131
|
-
setBaseUrl(defaults.endpoint);
|
|
132
|
-
let gitAuthor;
|
|
133
|
-
try {
|
|
134
|
-
const user = await getCurrentUser({ token });
|
|
135
|
-
gitAuthor = `${user.full_name || user.login} <${user.email}>`;
|
|
136
|
-
botUserID = user.id;
|
|
137
|
-
botUserName = user.login;
|
|
138
|
-
const env = getEnv();
|
|
139
|
-
/* v8 ignore if: experimental feature */
|
|
140
|
-
if (semver.valid(env.RENOVATE_X_PLATFORM_VERSION)) defaults.version = env.RENOVATE_X_PLATFORM_VERSION;
|
|
141
|
-
else defaults.version = await getVersion({ token });
|
|
142
|
-
logger.debug(`Forgejo version: ${defaults.version}`);
|
|
143
|
-
} catch (err) {
|
|
144
|
-
logger.debug({ err }, "Error authenticating with Forgejo. Check your token");
|
|
145
|
-
throw new Error("Init: Authentication failure");
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
endpoint: defaults.endpoint,
|
|
149
|
-
gitAuthor
|
|
150
|
-
};
|
|
151
|
-
},
|
|
152
|
-
async getRawFile(fileName, repoName, branchOrTag) {
|
|
153
|
-
const repo = repoName ?? config.repository;
|
|
154
|
-
const contents = await getRepoContents(repo, fileName, branchOrTag);
|
|
155
|
-
if (contents.type !== "file") return null;
|
|
156
|
-
return contents.contentString ?? null;
|
|
157
|
-
},
|
|
158
|
-
async getJsonFile(fileName, repoName, branchOrTag) {
|
|
159
|
-
const raw = await platform.getRawFile(fileName, repoName, branchOrTag);
|
|
160
|
-
return parseJson(raw, fileName);
|
|
161
|
-
},
|
|
162
|
-
async initRepo({ repository, cloneSubmodules, cloneSubmodulesFilter, gitUrl }) {
|
|
163
|
-
let repo;
|
|
164
|
-
config = {};
|
|
165
|
-
config.repository = repository;
|
|
166
|
-
config.cloneSubmodules = !!cloneSubmodules;
|
|
167
|
-
config.cloneSubmodulesFilter = cloneSubmodulesFilter;
|
|
168
|
-
config.ignorePrAuthor = GlobalConfig.get("ignorePrAuthor");
|
|
169
|
-
try {
|
|
170
|
-
repo = await getRepo(repository);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
logger.debug({ err }, "Unknown Forgejo initRepo error");
|
|
173
|
-
throw err;
|
|
174
|
-
}
|
|
175
|
-
if (repo.archived) {
|
|
176
|
-
logger.debug("Repository is archived - aborting renovation");
|
|
177
|
-
throw new Error(REPOSITORY_ARCHIVED);
|
|
178
|
-
}
|
|
179
|
-
if (repo.mirror) {
|
|
180
|
-
logger.debug("Repository is a mirror - aborting renovation");
|
|
181
|
-
throw new Error(REPOSITORY_MIRRORED);
|
|
182
|
-
}
|
|
183
|
-
if (!repo.permissions.pull || !repo.permissions.push) {
|
|
184
|
-
logger.debug("Repository does not permit pull or push - aborting renovation");
|
|
185
|
-
throw new Error(REPOSITORY_ACCESS_FORBIDDEN);
|
|
186
|
-
}
|
|
187
|
-
if (repo.empty) {
|
|
188
|
-
logger.debug("Repository is empty - aborting renovation");
|
|
189
|
-
throw new Error(REPOSITORY_EMPTY);
|
|
190
|
-
}
|
|
191
|
-
if (repo.has_pull_requests === false) {
|
|
192
|
-
logger.debug("Repo has disabled pull requests - aborting renovation");
|
|
193
|
-
throw new Error(REPOSITORY_BLOCKED);
|
|
194
|
-
}
|
|
195
|
-
const mergeStyle = [
|
|
196
|
-
...repo.default_merge_style ? [repo.default_merge_style] : [],
|
|
197
|
-
"fast-forward-only",
|
|
198
|
-
"squash",
|
|
199
|
-
"merge",
|
|
200
|
-
"rebase",
|
|
201
|
-
"rebase-merge"
|
|
202
|
-
].find((style) => isAllowed(style, repo));
|
|
203
|
-
if (mergeStyle) config.mergeMethod = mergeStyle;
|
|
204
|
-
else {
|
|
205
|
-
logger.debug("Repository has no allowed merge methods - aborting renovation");
|
|
206
|
-
throw new Error(REPOSITORY_BLOCKED);
|
|
207
|
-
}
|
|
208
|
-
try {
|
|
209
|
-
config.isOrgRepo = await isOrg(repo.owner.login);
|
|
210
|
-
} catch (err) {
|
|
211
|
-
logger.debug({ err }, "Forgejo initRepo() error");
|
|
212
|
-
throw err;
|
|
213
|
-
}
|
|
214
|
-
config.defaultBranch = repo.default_branch;
|
|
215
|
-
logger.debug(`${repository} default branch = ${config.defaultBranch}`);
|
|
216
|
-
const url = getRepoUrl(repo, gitUrl, defaults.endpoint);
|
|
217
|
-
await initRepo$1({
|
|
218
|
-
...config,
|
|
219
|
-
url
|
|
220
|
-
});
|
|
221
|
-
config.issueList = null;
|
|
222
|
-
config.labelList = null;
|
|
223
|
-
config.hasIssuesEnabled = !repo.external_tracker && repo.has_issues;
|
|
224
|
-
config.orgName = repo.owner.login;
|
|
225
|
-
return {
|
|
226
|
-
defaultBranch: config.defaultBranch,
|
|
227
|
-
isFork: !!repo.fork,
|
|
228
|
-
repoFingerprint: repoFingerprint(repo.id, defaults.endpoint)
|
|
229
|
-
};
|
|
230
|
-
},
|
|
231
|
-
async getRepos(config) {
|
|
232
|
-
logger.debug("Auto-discovering Forgejo repositories");
|
|
233
|
-
try {
|
|
234
|
-
if (config?.topics) {
|
|
235
|
-
logger.debug({ topics: config.topics }, "Auto-discovering by topics");
|
|
236
|
-
const fetchRepoArgs = config.topics.map((topic) => {
|
|
237
|
-
return {
|
|
238
|
-
topic,
|
|
239
|
-
sort: config.sort,
|
|
240
|
-
order: config.order
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
const repos = await map(fetchRepoArgs, fetchRepositories);
|
|
244
|
-
return deduplicateArray(repos.flat());
|
|
245
|
-
}
|
|
246
|
-
if (config?.namespaces) {
|
|
247
|
-
logger.debug({ namespaces: config.namespaces }, "Auto-discovering by organization");
|
|
248
|
-
const repos = await map(config.namespaces, async (organization) => {
|
|
249
|
-
return (await orgListRepos(organization)).filter((r) => !r.mirror && !r.archived).map((r) => r.full_name);
|
|
250
|
-
});
|
|
251
|
-
return deduplicateArray(repos.flat());
|
|
252
|
-
}
|
|
253
|
-
return await fetchRepositories({
|
|
254
|
-
sort: config?.sort,
|
|
255
|
-
order: config?.order
|
|
256
|
-
});
|
|
257
|
-
} catch (err) {
|
|
258
|
-
logger.error({ err }, "Forgejo getRepos() error");
|
|
259
|
-
throw err;
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
async setBranchStatus({ branchName, context, description, state, url: target_url }) {
|
|
263
|
-
try {
|
|
264
|
-
const branchCommit = getBranchCommit(branchName);
|
|
265
|
-
await createCommitStatus(config.repository, branchCommit, {
|
|
266
|
-
state: renovateToForgejoStatusMapping[state] || "pending",
|
|
267
|
-
context,
|
|
268
|
-
description,
|
|
269
|
-
...target_url && { target_url }
|
|
270
|
-
});
|
|
271
|
-
await getCombinedCommitStatus(config.repository, branchName, { memCache: false });
|
|
272
|
-
} catch (err) {
|
|
273
|
-
logger.warn({ err }, "Failed to set branch status");
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
async getBranchStatus(branchName, internalChecksAsSuccess) {
|
|
277
|
-
let ccs;
|
|
278
|
-
try {
|
|
279
|
-
ccs = await getCombinedCommitStatus(config.repository, branchName);
|
|
280
|
-
} catch (err) {
|
|
281
|
-
if (err.statusCode === 404) {
|
|
282
|
-
logger.debug("Received 404 when checking branch status, assuming branch deletion");
|
|
283
|
-
throw new Error(REPOSITORY_CHANGED);
|
|
284
|
-
}
|
|
285
|
-
logger.debug("Unknown error when checking branch status");
|
|
286
|
-
throw err;
|
|
287
|
-
}
|
|
288
|
-
logger.debug({ ccs }, "Branch status check result");
|
|
289
|
-
if (!internalChecksAsSuccess && ccs.worstStatus === "success" && ccs.statuses.every((status) => status.context?.startsWith("renovate/"))) {
|
|
290
|
-
logger.debug("Successful checks are all internal renovate/ checks, so returning \"pending\" branch status");
|
|
291
|
-
return "yellow";
|
|
292
|
-
}
|
|
293
|
-
return forgejoToRenovateStatusMapping[ccs.worstStatus];
|
|
294
|
-
},
|
|
295
|
-
async getBranchStatusCheck(branchName, context) {
|
|
296
|
-
const cs = (await getCombinedCommitStatus(config.repository, branchName)).statuses.find((s) => s.context === context);
|
|
297
|
-
if (!cs) return null;
|
|
298
|
-
return forgejoToRenovateStatusMapping[cs.status];
|
|
299
|
-
},
|
|
300
|
-
getPrList() {
|
|
301
|
-
return ForgejoPrCache.getPrs(forgejoHttp, config.repository, config.ignorePrAuthor, botUserName);
|
|
302
|
-
},
|
|
303
|
-
async getPr(number) {
|
|
304
|
-
let pr = (await platform.getPrList()).find((p) => p.number === number) ?? null;
|
|
305
|
-
if (pr) logger.debug("Returning from cached PRs");
|
|
306
|
-
else {
|
|
307
|
-
logger.debug("PR not found in cached PRs - trying to fetch directly");
|
|
308
|
-
const gpr = await getPR(config.repository, number);
|
|
309
|
-
pr = toRenovatePR(gpr, botUserName);
|
|
310
|
-
if (pr) await ForgejoPrCache.setPr(forgejoHttp, config.repository, config.ignorePrAuthor, botUserName, pr);
|
|
311
|
-
}
|
|
312
|
-
if (!pr) return null;
|
|
313
|
-
return pr;
|
|
314
|
-
},
|
|
315
|
-
async findPr({ branchName, prTitle: title, state = "all", includeOtherAuthors, targetBranch }) {
|
|
316
|
-
logger.debug(`findPr(${branchName}, ${title}, ${state})`);
|
|
317
|
-
if (includeOtherAuthors && isString(targetBranch)) {
|
|
318
|
-
const pr = await getPRByBranch(config.repository, targetBranch, branchName);
|
|
319
|
-
if (!pr) return null;
|
|
320
|
-
return toRenovatePR(pr, null);
|
|
321
|
-
}
|
|
322
|
-
const pr = (await platform.getPrList()).find((p) => p.sourceRepo === config.repository && p.sourceBranch === branchName && matchesState(p.state, state) && (!title || p.title === title));
|
|
323
|
-
if (pr) logger.debug(`Found PR #${pr.number}`);
|
|
324
|
-
return pr ?? null;
|
|
325
|
-
},
|
|
326
|
-
async createPr({ sourceBranch, targetBranch, prTitle, prBody: rawBody, labels: labelNames, platformPrOptions, draftPR }) {
|
|
327
|
-
let title = prTitle;
|
|
328
|
-
const base = targetBranch;
|
|
329
|
-
const head = sourceBranch;
|
|
330
|
-
const body = sanitize(rawBody);
|
|
331
|
-
if (draftPR) title = DRAFT_PREFIX + title;
|
|
332
|
-
logger.debug(`Creating pull request: ${title} (${head} => ${base})`);
|
|
333
|
-
try {
|
|
334
|
-
const labels = Array.isArray(labelNames) ? await map(labelNames, lookupLabelByName) : [];
|
|
335
|
-
const gpr = await createPR(config.repository, {
|
|
336
|
-
base,
|
|
337
|
-
head,
|
|
338
|
-
title,
|
|
339
|
-
body,
|
|
340
|
-
labels: labels.filter(isNumber)
|
|
341
|
-
});
|
|
342
|
-
if (platformPrOptions?.usePlatformAutomerge) {
|
|
343
|
-
if (semver.gte(defaults.version, "10.0.0-0")) try {
|
|
344
|
-
await mergePR(config.repository, gpr.number, {
|
|
345
|
-
Do: getMergeMethod(platformPrOptions?.automergeStrategy) ?? config.mergeMethod,
|
|
346
|
-
merge_when_checks_succeed: true,
|
|
347
|
-
delete_branch_after_merge: true
|
|
348
|
-
});
|
|
349
|
-
logger.debug({ prNumber: gpr.number }, "Forgejo-native automerge: success");
|
|
350
|
-
} catch (err) {
|
|
351
|
-
logger.warn({
|
|
352
|
-
err,
|
|
353
|
-
prNumber: gpr.number
|
|
354
|
-
}, "Forgejo-native automerge: fail");
|
|
355
|
-
}
|
|
356
|
-
else logger.debug({ prNumber: gpr.number }, `Forgejo-native automerge: not supported on this version of Forgejo. Use 10.0.0 or newer.`);
|
|
357
|
-
}
|
|
358
|
-
const pr = toRenovatePR(gpr, botUserName);
|
|
359
|
-
if (!pr) throw new Error("Can not parse newly created Pull Request");
|
|
360
|
-
await ForgejoPrCache.setPr(forgejoHttp, config.repository, config.ignorePrAuthor, botUserName, pr);
|
|
361
|
-
return pr;
|
|
362
|
-
} catch (err) {
|
|
363
|
-
if (err.statusCode === 409) {
|
|
364
|
-
logger.warn({
|
|
365
|
-
prTitle: title,
|
|
366
|
-
sourceBranch
|
|
367
|
-
}, "Attempting to gracefully recover from 409 Conflict response in createPr()");
|
|
368
|
-
ForgejoPrCache.forceSync();
|
|
369
|
-
const pr = await platform.findPr({
|
|
370
|
-
branchName: sourceBranch,
|
|
371
|
-
state: "open"
|
|
372
|
-
});
|
|
373
|
-
// v8 ignore else -- TODO: add test #40625
|
|
374
|
-
if (pr?.bodyStruct) {
|
|
375
|
-
if (pr.title !== title || pr.bodyStruct.hash !== hashBody(body)) {
|
|
376
|
-
logger.debug(`Recovered from 409 Conflict, but PR for ${sourceBranch} is outdated. Updating...`);
|
|
377
|
-
await platform.updatePr({
|
|
378
|
-
number: pr.number,
|
|
379
|
-
prTitle: title,
|
|
380
|
-
prBody: body
|
|
381
|
-
});
|
|
382
|
-
pr.title = title;
|
|
383
|
-
pr.bodyStruct = getPrBodyStruct(body);
|
|
384
|
-
} else logger.debug(`Recovered from 409 Conflict and PR for ${sourceBranch} is up-to-date`);
|
|
385
|
-
return pr;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
throw err;
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
async updatePr({ number, prTitle, prBody: body, labels, state, targetBranch }) {
|
|
392
|
-
let title = prTitle;
|
|
393
|
-
if ((await getPrList()).find((pr) => pr.number === number)?.isDraft) title = DRAFT_PREFIX + title;
|
|
394
|
-
const prUpdateParams = {
|
|
395
|
-
title,
|
|
396
|
-
...body && { body },
|
|
397
|
-
...state && { state }
|
|
398
|
-
};
|
|
399
|
-
if (targetBranch) prUpdateParams.base = targetBranch;
|
|
400
|
-
/**
|
|
401
|
-
* Update PR labels.
|
|
402
|
-
* In the Forgejo API, labels are replaced on each update if the field is present.
|
|
403
|
-
* If the field is not present (i.e., undefined), labels aren't updated.
|
|
404
|
-
* However, the labels array must contain label IDs instead of names,
|
|
405
|
-
* so a lookup is performed to fetch the details (including the ID) of each label.
|
|
406
|
-
*/
|
|
407
|
-
if (Array.isArray(labels)) {
|
|
408
|
-
prUpdateParams.labels = (await map(labels, lookupLabelByName)).filter(isNumber);
|
|
409
|
-
if (labels.length !== prUpdateParams.labels.length) logger.warn("Some labels could not be looked up. Renovate may halt label updates assuming changes by others.");
|
|
410
|
-
}
|
|
411
|
-
const gpr = await updatePR(config.repository, number, prUpdateParams);
|
|
412
|
-
const pr = toRenovatePR(gpr, botUserName);
|
|
413
|
-
if (pr) await ForgejoPrCache.setPr(forgejoHttp, config.repository, config.ignorePrAuthor, botUserName, pr);
|
|
414
|
-
},
|
|
415
|
-
async mergePr({ id, strategy }) {
|
|
416
|
-
try {
|
|
417
|
-
await mergePR(config.repository, id, { Do: getMergeMethod(strategy) ?? config.mergeMethod });
|
|
418
|
-
return true;
|
|
419
|
-
} catch (err) {
|
|
420
|
-
logger.warn({
|
|
421
|
-
err,
|
|
422
|
-
id
|
|
423
|
-
}, "Merging of PR failed");
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
getIssueList() {
|
|
428
|
-
if (config.hasIssuesEnabled === false) return Promise.resolve([]);
|
|
429
|
-
config.issueList ??= searchIssues(config.repository, { state: "all" }, { memCache: false }).then((issues) => {
|
|
430
|
-
const issueList = issues.map(toRenovateIssue);
|
|
431
|
-
logger.debug(`Retrieved ${issueList.length} Issues`);
|
|
432
|
-
return issueList;
|
|
433
|
-
});
|
|
434
|
-
return config.issueList;
|
|
435
|
-
},
|
|
436
|
-
async getIssue(number, memCache = true) {
|
|
437
|
-
if (config.hasIssuesEnabled === false) return null;
|
|
438
|
-
try {
|
|
439
|
-
return {
|
|
440
|
-
number,
|
|
441
|
-
body: (await getIssue$1(config.repository, number, { memCache })).body
|
|
442
|
-
};
|
|
443
|
-
} catch (err) {
|
|
444
|
-
logger.debug({
|
|
445
|
-
err,
|
|
446
|
-
number
|
|
447
|
-
}, "Error getting issue");
|
|
448
|
-
return null;
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
async findIssue(title) {
|
|
452
|
-
const issue = (await platform.getIssueList()).find((i) => i.state === "open" && i.title === title);
|
|
453
|
-
if (!issue) return null;
|
|
454
|
-
logger.debug(`Found Issue #${issue.number}`);
|
|
455
|
-
return getIssue(issue.number);
|
|
456
|
-
},
|
|
457
|
-
async ensureIssue({ title, reuseTitle, body: content, labels: labelNames, shouldReOpen, once }) {
|
|
458
|
-
logger.debug(`ensureIssue(${title})`);
|
|
459
|
-
if (config.hasIssuesEnabled === false) {
|
|
460
|
-
logger.info("Cannot ensure issue because issues are disabled in this repository");
|
|
461
|
-
return null;
|
|
462
|
-
}
|
|
463
|
-
try {
|
|
464
|
-
const body = smartLinks(content);
|
|
465
|
-
const issueList = await platform.getIssueList();
|
|
466
|
-
let issues = issueList.filter((i) => i.title === title);
|
|
467
|
-
if (!issues.length) issues = issueList.filter((i) => i.title === reuseTitle);
|
|
468
|
-
const labels = Array.isArray(labelNames) ? (await Promise.all(labelNames.map(lookupLabelByName))).filter(isNumber) : void 0;
|
|
469
|
-
if (issues.length) {
|
|
470
|
-
let activeIssue = issues.find((i) => i.state === "open");
|
|
471
|
-
if (!activeIssue) {
|
|
472
|
-
if (once) {
|
|
473
|
-
logger.debug("Issue already closed - skipping update");
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
if (shouldReOpen) logger.debug("Reopening previously closed Issue");
|
|
477
|
-
activeIssue = issues.at(-1);
|
|
478
|
-
}
|
|
479
|
-
for (const issue of issues) if (issue.state === "open" && issue.number !== activeIssue.number) {
|
|
480
|
-
logger.warn({ issueNo: issue.number }, "Closing duplicate issue");
|
|
481
|
-
await closeIssue(config.repository, issue.number);
|
|
482
|
-
}
|
|
483
|
-
if (activeIssue.title === title && activeIssue.body === body && activeIssue.state === "open") {
|
|
484
|
-
logger.debug(`Issue #${activeIssue.number} is open and up to date - nothing to do`);
|
|
485
|
-
return null;
|
|
486
|
-
}
|
|
487
|
-
if (shouldReOpen || activeIssue.state === "open") {
|
|
488
|
-
logger.debug(`Updating Issue #${activeIssue.number}`);
|
|
489
|
-
const existingIssue = await updateIssue(config.repository, activeIssue.number, {
|
|
490
|
-
body,
|
|
491
|
-
title,
|
|
492
|
-
state: "open"
|
|
493
|
-
});
|
|
494
|
-
const existingLabelIds = coerceArray(existingIssue.labels).map((label) => label.id);
|
|
495
|
-
if (labels && (labels.length !== existingLabelIds.length || labels.filter((labelId) => !existingLabelIds.includes(labelId)).length !== 0)) await updateIssueLabels(config.repository, activeIssue.number, { labels });
|
|
496
|
-
return "updated";
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
const issue = await createIssue(config.repository, {
|
|
500
|
-
body,
|
|
501
|
-
title,
|
|
502
|
-
labels
|
|
503
|
-
});
|
|
504
|
-
logger.debug(`Created new Issue #${issue.number}`);
|
|
505
|
-
config.issueList = null;
|
|
506
|
-
return "created";
|
|
507
|
-
} catch (err) {
|
|
508
|
-
logger.warn({ err }, "Could not ensure issue");
|
|
509
|
-
}
|
|
510
|
-
return null;
|
|
511
|
-
},
|
|
512
|
-
async ensureIssueClosing(title) {
|
|
513
|
-
logger.debug(`ensureIssueClosing(${title})`);
|
|
514
|
-
if (config.hasIssuesEnabled === false) return;
|
|
515
|
-
const issueList = await platform.getIssueList();
|
|
516
|
-
for (const issue of issueList) if (issue.state === "open" && issue.title === title) {
|
|
517
|
-
logger.debug(`Closing issue...issueNo: ${issue.number}`);
|
|
518
|
-
await closeIssue(config.repository, issue.number);
|
|
519
|
-
}
|
|
520
|
-
},
|
|
521
|
-
async deleteLabel(issue, labelName) {
|
|
522
|
-
logger.debug(`Deleting label ${labelName} from Issue #${issue}`);
|
|
523
|
-
const label = await lookupLabelByName(labelName);
|
|
524
|
-
if (label) await unassignLabel(config.repository, issue, label);
|
|
525
|
-
else logger.warn({
|
|
526
|
-
issue,
|
|
527
|
-
labelName
|
|
528
|
-
}, "Failed to lookup label for deletion");
|
|
529
|
-
},
|
|
530
|
-
async ensureComment({ number: issue, topic, content }) {
|
|
531
|
-
try {
|
|
532
|
-
let body = sanitize(content);
|
|
533
|
-
const commentList = await getComments(config.repository, issue);
|
|
534
|
-
let comment = null;
|
|
535
|
-
if (topic) {
|
|
536
|
-
comment = findCommentByTopic(commentList, topic);
|
|
537
|
-
body = `### ${topic}\n\n${body}`;
|
|
538
|
-
} else comment = findCommentByContent(commentList, body);
|
|
539
|
-
if (!comment) {
|
|
540
|
-
comment = await createComment(config.repository, issue, body);
|
|
541
|
-
logger.info({
|
|
542
|
-
repository: config.repository,
|
|
543
|
-
issue,
|
|
544
|
-
comment: comment.id
|
|
545
|
-
}, "Comment added");
|
|
546
|
-
} else if (comment.body === body) logger.debug(`Comment #${comment.id} is already up-to-date`);
|
|
547
|
-
else {
|
|
548
|
-
await updateComment(config.repository, comment.id, body);
|
|
549
|
-
logger.debug({
|
|
550
|
-
repository: config.repository,
|
|
551
|
-
issue,
|
|
552
|
-
comment: comment.id
|
|
553
|
-
}, "Comment updated");
|
|
554
|
-
}
|
|
555
|
-
return true;
|
|
556
|
-
} catch (err) {
|
|
557
|
-
logger.warn({
|
|
558
|
-
err,
|
|
559
|
-
issue,
|
|
560
|
-
subject: topic
|
|
561
|
-
}, "Error ensuring comment");
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
},
|
|
565
|
-
async ensureCommentRemoval(deleteConfig) {
|
|
566
|
-
const { number: issue } = deleteConfig;
|
|
567
|
-
const key = deleteConfig.type === "by-topic" ? deleteConfig.topic : deleteConfig.content;
|
|
568
|
-
logger.debug(`Ensuring comment "${key}" in #${issue} is removed`);
|
|
569
|
-
const commentList = await getComments(config.repository, issue);
|
|
570
|
-
let comment = null;
|
|
571
|
-
// v8 ignore else -- TODO: add test #40625
|
|
572
|
-
if (deleteConfig.type === "by-topic") comment = findCommentByTopic(commentList, deleteConfig.topic);
|
|
573
|
-
else if (deleteConfig.type === "by-content") comment = findCommentByContent(commentList, sanitize(deleteConfig.content));
|
|
574
|
-
if (!comment) return;
|
|
575
|
-
try {
|
|
576
|
-
await deleteComment(config.repository, comment.id);
|
|
577
|
-
} catch (err) {
|
|
578
|
-
logger.warn({
|
|
579
|
-
err,
|
|
580
|
-
issue,
|
|
581
|
-
config: deleteConfig
|
|
582
|
-
}, "Error deleting comment");
|
|
583
|
-
}
|
|
584
|
-
},
|
|
585
|
-
async getBranchPr(branchName) {
|
|
586
|
-
logger.debug(`getBranchPr(${branchName})`);
|
|
587
|
-
const pr = await platform.findPr({
|
|
588
|
-
branchName,
|
|
589
|
-
state: "open"
|
|
590
|
-
});
|
|
591
|
-
return pr ? platform.getPr(pr.number) : null;
|
|
592
|
-
},
|
|
593
|
-
async addAssignees(number, assignees) {
|
|
594
|
-
logger.debug(`Updating assignees '${assignees?.join(", ")}' on Issue #${number}`);
|
|
595
|
-
await updateIssue(config.repository, number, { assignees });
|
|
596
|
-
},
|
|
597
|
-
async addReviewers(number, reviewers) {
|
|
598
|
-
logger.debug(`Adding reviewers '${reviewers?.join(", ")}' to #${number}`);
|
|
599
|
-
try {
|
|
600
|
-
const teamReviewers = new Set(reviewers.filter((r) => r.startsWith("team:")).map((r) => r.substring(5)));
|
|
601
|
-
const userReviewers = new Set(reviewers.filter((r) => !r.startsWith("team:")));
|
|
602
|
-
await requestPrReviewers(config.repository, number, {
|
|
603
|
-
reviewers: [...userReviewers],
|
|
604
|
-
...teamReviewers.size && { team_reviewers: [...teamReviewers] }
|
|
605
|
-
});
|
|
606
|
-
} catch (err) {
|
|
607
|
-
logger.warn({
|
|
608
|
-
err,
|
|
609
|
-
number,
|
|
610
|
-
reviewers
|
|
611
|
-
}, "Failed to assign reviewer");
|
|
612
|
-
}
|
|
613
|
-
},
|
|
614
|
-
massageMarkdown(prBody) {
|
|
615
|
-
return smartTruncate(smartLinks(prBody), maxBodyLength());
|
|
616
|
-
},
|
|
617
|
-
maxBodyLength
|
|
618
|
-
};
|
|
619
|
-
function maxBodyLength() {
|
|
620
|
-
return 1e6;
|
|
621
|
-
}
|
|
622
|
-
const { addAssignees, addReviewers, createPr, deleteLabel, ensureComment, ensureCommentRemoval, ensureIssue, ensureIssueClosing, findIssue, findPr, getBranchPr, getBranchStatus, getBranchStatusCheck, getIssue, getRawFile, getJsonFile, getIssueList, getPr, massageMarkdown, getPrList, getRepos, initPlatform, initRepo, mergePr, setBranchStatus, updatePr } = platform;
|
|
40
|
+
const forgejoHttp = new ForgejoHttp();
|
|
41
|
+
function logDetectedVersion(version) {
|
|
42
|
+
logger.debug(`Forgejo version: ${version}`);
|
|
43
|
+
}
|
|
44
|
+
function checkNativeAutomerge(version) {
|
|
45
|
+
if (semver.gte(version, "10.0.0-0")) return null;
|
|
46
|
+
return `Forgejo-native automerge: not supported on this version of Forgejo. Use 10.0.0 or newer.`;
|
|
47
|
+
}
|
|
48
|
+
const { platform, resetPlatform } = createPlatform({
|
|
49
|
+
id,
|
|
50
|
+
defaultEndpoint: "https://code.forgejo.org/",
|
|
51
|
+
http: forgejoHttp,
|
|
52
|
+
setBaseUrl,
|
|
53
|
+
logDetectedVersion,
|
|
54
|
+
checkNativeAutomerge
|
|
55
|
+
});
|
|
56
|
+
const { addAssignees, addReviewers, createPr, deleteLabel, ensureComment, ensureCommentRemoval, ensureIssue, ensureIssueClosing, findIssue, findPr, getBranchPr, getBranchStatus, getBranchStatusCheck, getIssue, getRawFile, getJsonFile, getIssueList, getPr, massageMarkdown, maxBodyLength, getPrList, getRepos, initPlatform, initRepo, mergePr, setBranchStatus, updatePr } = platform;
|
|
623
57
|
//#endregion
|
|
624
|
-
export { addAssignees, addReviewers, createPr, deleteLabel, ensureComment, ensureCommentRemoval, ensureIssue, ensureIssueClosing, findIssue, findPr, forgejo_exports, getBranchPr, getBranchStatus, getBranchStatusCheck, getIssue, getIssueList, getJsonFile, getPr, getPrList, getRawFile, getRepos, id, initPlatform, initRepo, massageMarkdown, maxBodyLength, mergePr, resetPlatform, setBranchStatus, updatePr };
|
|
58
|
+
export { addAssignees, addReviewers, createPr, deleteLabel, ensureComment, ensureCommentRemoval, ensureIssue, ensureIssueClosing, findIssue, findPr, forgejoHttp, forgejo_exports, getBranchPr, getBranchStatus, getBranchStatusCheck, getIssue, getIssueList, getJsonFile, getPr, getPrList, getRawFile, getRepos, id, initPlatform, initRepo, massageMarkdown, maxBodyLength, mergePr, resetPlatform, setBranchStatus, updatePr };
|
|
625
59
|
|
|
626
60
|
//# sourceMappingURL=index.js.map
|