mirror of
https://github.com/yusufipk/RepoHub.git
synced 2026-09-11 10:36:07 +00:00
docs: Add comprehensive package contribution guide and validation tooling
- Add bilingual README with English/Turkish language switcher - Create detailed package contribution guidelines with platform-specific naming conventions - Add validation script for verifying package presets against database - Include best practices and example PR format for contributors - Add tsx dependency for running TypeScript validation scripts - Document validation workflow with manual and automated verification options
This commit is contained in:
Executable
+188
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Validate package names in recommendation presets against the database
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/validate-presets.js --all
|
||||
* node scripts/validate-presets.js windows macos
|
||||
* node scripts/validate-presets.js ubuntu
|
||||
*/
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
// Import the presets
|
||||
const { PACKAGE_PRESETS } = require('../src/data/recommendationPresets.ts');
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
// Platform IDs
|
||||
const PLATFORMS = ['windows', 'macos', 'ubuntu', 'debian', 'arch', 'fedora'];
|
||||
|
||||
async function checkPackageExists(platformId, packageName) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/api/packages?platform_id=${platformId}&search=${encodeURIComponent(packageName)}&limit=10`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Check for exact match (case-insensitive)
|
||||
const exactMatch = data.packages?.find(
|
||||
pkg => pkg.name.toLowerCase() === packageName.toLowerCase()
|
||||
);
|
||||
|
||||
return {
|
||||
exists: !!exactMatch,
|
||||
foundName: exactMatch?.name || null,
|
||||
similarMatches: data.packages?.slice(0, 3).map(p => p.name) || []
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error checking ${packageName} on ${platformId}:`, error.message);
|
||||
return { exists: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function validatePlatform(platformId) {
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`Validating ${platformId.toUpperCase()}`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const platformPresets = PACKAGE_PRESETS[platformId];
|
||||
if (!platformPresets) {
|
||||
console.log(`❌ No presets found for platform: ${platformId}`);
|
||||
return { total: 0, found: 0, missing: [] };
|
||||
}
|
||||
|
||||
const results = {
|
||||
total: 0,
|
||||
found: 0,
|
||||
missing: []
|
||||
};
|
||||
|
||||
// Check each category
|
||||
for (const [category, packages] of Object.entries(platformPresets)) {
|
||||
console.log(`\n📁 Category: ${category}`);
|
||||
|
||||
for (const packageName of packages) {
|
||||
results.total++;
|
||||
|
||||
const check = await checkPackageExists(platformId, packageName);
|
||||
|
||||
if (check.error) {
|
||||
console.log(` ⚠️ ${packageName} - Error: ${check.error}`);
|
||||
results.missing.push({ category, packageName, reason: 'API Error' });
|
||||
} else if (check.exists) {
|
||||
console.log(` ✅ ${packageName}${check.foundName !== packageName ? ` (found as: ${check.foundName})` : ''}`);
|
||||
results.found++;
|
||||
} else {
|
||||
console.log(` ❌ ${packageName} - NOT FOUND`);
|
||||
if (check.similarMatches?.length > 0) {
|
||||
console.log(` Similar: ${check.similarMatches.join(', ')}`);
|
||||
}
|
||||
results.missing.push({
|
||||
category,
|
||||
packageName,
|
||||
similar: check.similarMatches
|
||||
});
|
||||
}
|
||||
|
||||
// Small delay to avoid overwhelming the API
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.log('Usage:');
|
||||
console.log(' node scripts/validate-presets.js --all');
|
||||
console.log(' node scripts/validate-presets.js windows macos ubuntu');
|
||||
console.log('\nAvailable platforms:', PLATFORMS.join(', '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let platformsToCheck = [];
|
||||
|
||||
if (args.includes('--all')) {
|
||||
platformsToCheck = PLATFORMS;
|
||||
} else {
|
||||
// Validate platform names
|
||||
for (const platform of args) {
|
||||
if (!PLATFORMS.includes(platform)) {
|
||||
console.error(`❌ Invalid platform: ${platform}`);
|
||||
console.log('Available platforms:', PLATFORMS.join(', '));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
platformsToCheck = args;
|
||||
}
|
||||
|
||||
console.log(`\n🔍 Validating package presets for: ${platformsToCheck.join(', ')}\n`);
|
||||
|
||||
const allResults = {};
|
||||
|
||||
for (const platform of platformsToCheck) {
|
||||
const results = await validatePlatform(platform);
|
||||
allResults[platform] = results;
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log('SUMMARY');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
let totalPackages = 0;
|
||||
let totalFound = 0;
|
||||
let totalMissing = 0;
|
||||
|
||||
for (const [platform, results] of Object.entries(allResults)) {
|
||||
totalPackages += results.total;
|
||||
totalFound += results.found;
|
||||
totalMissing += results.missing.length;
|
||||
|
||||
const successRate = results.total > 0
|
||||
? ((results.found / results.total) * 100).toFixed(1)
|
||||
: 0;
|
||||
|
||||
console.log(`\n${platform.toUpperCase()}:`);
|
||||
console.log(` Total: ${results.total}`);
|
||||
console.log(` Found: ${results.found} (${successRate}%)`);
|
||||
console.log(` Missing: ${results.missing.length}`);
|
||||
|
||||
if (results.missing.length > 0) {
|
||||
console.log(` Missing packages:`);
|
||||
for (const { category, packageName, similar } of results.missing) {
|
||||
console.log(` - ${packageName} (${category})`);
|
||||
if (similar?.length > 0) {
|
||||
console.log(` Try: ${similar.join(', ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`OVERALL: ${totalFound}/${totalPackages} packages found (${((totalFound / totalPackages) * 100).toFixed(1)}%)`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
if (totalMissing > 0) {
|
||||
console.log(`\n⚠️ Found ${totalMissing} missing packages. Review the output above and update recommendationPresets.ts`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n✅ All packages validated successfully!');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Validate package names in recommendation presets against the database
|
||||
*
|
||||
* Usage:
|
||||
* tsx scripts/validate-presets.ts --all
|
||||
* tsx scripts/validate-presets.ts windows macos
|
||||
* tsx scripts/validate-presets.ts ubuntu
|
||||
*/
|
||||
|
||||
import { PACKAGE_PRESETS } from '../src/data/recommendationPresets';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3002';
|
||||
|
||||
// Platform IDs
|
||||
const PLATFORMS = ['windows', 'macos', 'ubuntu', 'debian', 'arch', 'fedora'] as const;
|
||||
type PlatformId = typeof PLATFORMS[number];
|
||||
|
||||
interface PackageCheckResult {
|
||||
exists: boolean;
|
||||
foundName?: string;
|
||||
similarMatches?: string[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
async function checkPackageExists(
|
||||
platformId: string,
|
||||
packageName: string
|
||||
): Promise<PackageCheckResult> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/api/packages?platform_id=${platformId}&search=${encodeURIComponent(packageName)}&limit=10`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Check for exact match (case-insensitive)
|
||||
const exactMatch = data.packages?.find(
|
||||
(pkg: any) => pkg.name.toLowerCase() === packageName.toLowerCase()
|
||||
);
|
||||
|
||||
return {
|
||||
exists: !!exactMatch,
|
||||
foundName: exactMatch?.name || undefined,
|
||||
similarMatches: data.packages?.slice(0, 3).map((p: any) => p.name) || []
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error(`Error checking ${packageName} on ${platformId}:`, error.message);
|
||||
return { exists: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function validatePlatform(platformId: PlatformId) {
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`Validating ${platformId.toUpperCase()}`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const platformPresets = PACKAGE_PRESETS[platformId];
|
||||
if (!platformPresets) {
|
||||
console.log(`❌ No presets found for platform: ${platformId}`);
|
||||
return { total: 0, found: 0, missing: [] as any[] };
|
||||
}
|
||||
|
||||
const results = {
|
||||
total: 0,
|
||||
found: 0,
|
||||
missing: [] as { category: string; packageName: string; similar?: string[]; reason?: string }[]
|
||||
};
|
||||
|
||||
// Check each category
|
||||
for (const [category, packages] of Object.entries(platformPresets)) {
|
||||
console.log(`\n📁 Category: ${category}`);
|
||||
|
||||
for (const packageName of packages) {
|
||||
results.total++;
|
||||
|
||||
const check = await checkPackageExists(platformId, packageName);
|
||||
|
||||
if (check.error) {
|
||||
console.log(` ⚠️ ${packageName} - Error: ${check.error}`);
|
||||
results.missing.push({ category, packageName, reason: 'API Error' });
|
||||
} else if (check.exists) {
|
||||
console.log(` ✅ ${packageName}${check.foundName !== packageName ? ` (found as: ${check.foundName})` : ''}`);
|
||||
results.found++;
|
||||
} else {
|
||||
console.log(` ❌ ${packageName} - NOT FOUND`);
|
||||
if (check.similarMatches && check.similarMatches.length > 0) {
|
||||
console.log(` Similar: ${check.similarMatches.join(', ')}`);
|
||||
}
|
||||
results.missing.push({
|
||||
category,
|
||||
packageName,
|
||||
similar: check.similarMatches
|
||||
});
|
||||
}
|
||||
|
||||
// Delay to avoid rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.log('Usage:');
|
||||
console.log(' tsx scripts/validate-presets.ts --all');
|
||||
console.log(' tsx scripts/validate-presets.ts windows macos ubuntu');
|
||||
console.log('\nAvailable platforms:', PLATFORMS.join(', '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let platformsToCheck: PlatformId[] = [];
|
||||
|
||||
if (args.includes('--all')) {
|
||||
platformsToCheck = [...PLATFORMS];
|
||||
} else {
|
||||
// Validate platform names
|
||||
for (const platform of args) {
|
||||
if (!PLATFORMS.includes(platform as any)) {
|
||||
console.error(`❌ Invalid platform: ${platform}`);
|
||||
console.log('Available platforms:', PLATFORMS.join(', '));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
platformsToCheck = args as PlatformId[];
|
||||
}
|
||||
|
||||
console.log(`\n🔍 Validating package presets for: ${platformsToCheck.join(', ')}\n`);
|
||||
|
||||
const allResults: Record<string, any> = {};
|
||||
|
||||
for (const platform of platformsToCheck) {
|
||||
const results = await validatePlatform(platform);
|
||||
allResults[platform] = results;
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log('SUMMARY');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
let totalPackages = 0;
|
||||
let totalFound = 0;
|
||||
let totalMissing = 0;
|
||||
|
||||
for (const [platform, results] of Object.entries(allResults)) {
|
||||
totalPackages += results.total;
|
||||
totalFound += results.found;
|
||||
totalMissing += results.missing.length;
|
||||
|
||||
const successRate = results.total > 0
|
||||
? ((results.found / results.total) * 100).toFixed(1)
|
||||
: '0';
|
||||
|
||||
console.log(`\n${platform.toUpperCase()}:`);
|
||||
console.log(` Total: ${results.total}`);
|
||||
console.log(` Found: ${results.found} (${successRate}%)`);
|
||||
console.log(` Missing: ${results.missing.length}`);
|
||||
|
||||
if (results.missing.length > 0) {
|
||||
console.log(` Missing packages:`);
|
||||
for (const { category, packageName, similar } of results.missing) {
|
||||
console.log(` - ${packageName} (${category})`);
|
||||
if (similar && similar.length > 0) {
|
||||
console.log(` Try: ${similar.join(', ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`OVERALL: ${totalFound}/${totalPackages} packages found (${((totalFound / totalPackages) * 100).toFixed(1)}%)`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
if (totalMissing > 0) {
|
||||
console.log(`\n⚠️ Found ${totalMissing} missing packages. Review the output above and update recommendationPresets.ts`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n✅ All packages validated successfully!');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user