feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
238
CloudronPackages/APISIX/apisix-source/ci/check_changelog_prs.ts
Executable file
238
CloudronPackages/APISIX/apisix-source/ci/check_changelog_prs.ts
Executable file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
// Types
|
||||
interface Version {
|
||||
tag: string;
|
||||
ref: string;
|
||||
}
|
||||
|
||||
interface PR {
|
||||
number: number;
|
||||
title: string;
|
||||
commit: string;
|
||||
}
|
||||
|
||||
// Configuration
|
||||
const IGNORE_TYPES = [
|
||||
'docs',
|
||||
'chore',
|
||||
'test',
|
||||
'ci'
|
||||
];
|
||||
|
||||
const IGNORE_PRS = [
|
||||
// 3.9.0
|
||||
10655, 10857, 10858, 10887, 10959, 11029, 11041, 11053, 11055, 11061, 10976, 10984, 11025,
|
||||
// 3.10.0
|
||||
11105, 11128, 11169, 11171, 11280, 11333, 11081, 11202, 11469,
|
||||
// 3.11.0
|
||||
11463, 11570,
|
||||
// 3.12.0
|
||||
11769, 11816, 11881, 11905, 11924, 11926, 11973, 11991, 11992, 11829,
|
||||
// 3.13.0
|
||||
9945, 11420, 11765, 12036, 12048, 12057, 12076, 12122, 12123, 12168, 12199, 12218, 12225, 12272, 12277, 12300, 12306, 12329, 12353, 12364, 12375, 12358
|
||||
];
|
||||
|
||||
|
||||
function getGitRef(version: string): string {
|
||||
try {
|
||||
execSync(`git rev-parse ${version}`, { stdio: 'ignore' });
|
||||
return version;
|
||||
} catch {
|
||||
return 'HEAD';
|
||||
}
|
||||
}
|
||||
|
||||
function extractVersionsFromChangelog(): Version[] {
|
||||
const changelogPath = join(process.cwd(), '..', 'CHANGELOG.md');
|
||||
const content = readFileSync(changelogPath, 'utf-8');
|
||||
const versionRegex = /^## ([0-9]+\.[0-9]+\.[0-9]+)/gm;
|
||||
const versions: Version[] = [];
|
||||
let match;
|
||||
|
||||
while ((match = versionRegex.exec(content)) !== null) {
|
||||
const tag = match[1];
|
||||
versions.push({
|
||||
tag,
|
||||
ref: getGitRef(tag)
|
||||
});
|
||||
}
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
function extractPRsFromChangelog(startTag: string, endTag: string): number[] {
|
||||
const changelogPath = join(process.cwd(), '..', 'CHANGELOG.md');
|
||||
const content = readFileSync(changelogPath, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
let inRange = false;
|
||||
const prs: number[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith(`## ${startTag}`)) {
|
||||
inRange = true;
|
||||
continue;
|
||||
}
|
||||
if (inRange && line.startsWith(`## ${endTag}`)) {
|
||||
break;
|
||||
}
|
||||
if (inRange) {
|
||||
const match = line.match(/#(\d+)/);
|
||||
if (match) {
|
||||
prs.push(parseInt(match[1], 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prs.sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
function shouldIgnoreCommitMessage(message: string): boolean {
|
||||
// Extract the commit message part (remove the commit hash)
|
||||
const messagePart = message.split(' ').slice(1).join(' ');
|
||||
|
||||
// Check if the message starts with any of the ignored types
|
||||
for (const type of IGNORE_TYPES) {
|
||||
// Check simple format: "type: message"
|
||||
if (messagePart.startsWith(`${type}:`)) {
|
||||
return true;
|
||||
}
|
||||
// Check format with scope: "type(scope): message"
|
||||
if (messagePart.startsWith(`${type}(`)) {
|
||||
const closingBracketIndex = messagePart.indexOf('):');
|
||||
if (closingBracketIndex !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function extractPRsFromGitLog(oldRef: string, newRef: string): PR[] {
|
||||
const log = execSync(`git log ${oldRef}..${newRef} --oneline`, { encoding: 'utf-8' });
|
||||
const prs: PR[] = [];
|
||||
|
||||
for (const line of log.split('\n')) {
|
||||
if (!line.trim()) continue;
|
||||
|
||||
// Check if this commit should be ignored
|
||||
if (shouldIgnoreCommitMessage(line)) continue;
|
||||
|
||||
// Find PR number
|
||||
const prMatch = line.match(/#(\d+)/);
|
||||
if (prMatch) {
|
||||
const prNumber = parseInt(prMatch[1], 10);
|
||||
if (!IGNORE_PRS.includes(prNumber)) {
|
||||
prs.push({
|
||||
number: prNumber,
|
||||
title: line,
|
||||
commit: line.split(' ')[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prs.sort((a, b) => a.number - b.number);
|
||||
}
|
||||
|
||||
function findMissingPRs(changelogPRs: number[], gitPRs: PR[]): PR[] {
|
||||
const changelogPRSet = new Set(changelogPRs);
|
||||
return gitPRs.filter(pr => !changelogPRSet.has(pr.number));
|
||||
}
|
||||
|
||||
function versionGreaterThan(v1: string, v2: string): boolean {
|
||||
// Remove 'v' prefix if present
|
||||
const cleanV1 = v1.replace(/^v/, '');
|
||||
const cleanV2 = v2.replace(/^v/, '');
|
||||
|
||||
// Split version strings into arrays of numbers
|
||||
const v1Parts = cleanV1.split('.').map(Number);
|
||||
const v2Parts = cleanV2.split('.').map(Number);
|
||||
|
||||
// Compare each part
|
||||
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||
const v1Part = v1Parts[i] || 0;
|
||||
const v2Part = v2Parts[i] || 0;
|
||||
|
||||
if (v1Part > v2Part) return true;
|
||||
if (v1Part < v2Part) return false;
|
||||
}
|
||||
|
||||
// If all parts are equal, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
// Main function
|
||||
async function main() {
|
||||
try {
|
||||
const versions = extractVersionsFromChangelog();
|
||||
let hasErrors = false;
|
||||
|
||||
for (let i = 0; i < versions.length - 1; i++) {
|
||||
const newVersion = versions[i];
|
||||
const oldVersion = versions[i + 1];
|
||||
|
||||
// Skip if new version is less than or equal to 3.8.0
|
||||
if (!versionGreaterThan(newVersion.tag, '3.8.0')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\n=== Checking changes between ${newVersion.tag} (${newVersion.ref}) and ${oldVersion.tag} (${oldVersion.ref}) ===`);
|
||||
|
||||
const changelogPRs = extractPRsFromChangelog(newVersion.tag, oldVersion.tag);
|
||||
const gitPRs = extractPRsFromGitLog(oldVersion.ref, newVersion.ref);
|
||||
const missingPRs = findMissingPRs(changelogPRs, gitPRs);
|
||||
|
||||
console.log(`\n=== PR Comparison Results for ${newVersion.tag} ===`);
|
||||
|
||||
if (missingPRs.length === 0) {
|
||||
console.log(`\n✅ All PRs are included in CHANGELOG.md for version ${newVersion.tag}`);
|
||||
} else {
|
||||
console.log(`\n❌ Missing PRs in CHANGELOG.md for version ${newVersion.tag} (sorted):`);
|
||||
missingPRs.forEach(pr => {
|
||||
console.log(` #${pr.number}`);
|
||||
});
|
||||
|
||||
console.log(`\nDetailed information about missing PRs for version ${newVersion.tag}:`);
|
||||
missingPRs.forEach(pr => {
|
||||
console.log(`\nPR #${pr.number}:`);
|
||||
console.log(` - ${pr.title}`);
|
||||
console.log(` - PR URL: https://github.com/apache/apisix/pull/${pr.number}`);
|
||||
});
|
||||
|
||||
console.log('Note: If you confirm that a PR should not appear in the changelog, please add its number to the IGNORE_PRS array in this script.');
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await main();
|
||||
})();
|
Reference in New Issue
Block a user