-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild-repositories.js
More file actions
167 lines (140 loc) · 5.39 KB
/
Copy pathbuild-repositories.js
File metadata and controls
167 lines (140 loc) · 5.39 KB
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
#!/usr/bin/env node
/**
* Build repositories.json with categorization
* Fetches repos from GitHub and applies categorization logic from topic-analyzer.js
* Adds a 'categories' field with clean names while preserving original 'topics'
*/
const https = require('https');
const fs = require('fs');
const path = require('path');
const { analyzeRepository } = require('./topic-analyzer');
// GitHub API configuration
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const ORG_NAME = 'DecisionsDev';
const OUTPUT_PATH = path.join(__dirname, '../src/data/repositories.json');
// Repositories to exclude from the build
// .github: Special GitHub organization profile repository, not a project repository
// decisionsdev.github.io: This website repository itself
// sample-template_repository: Template repository, not a real project
const EXCLUDED_REPOS = ['.github', 'decisionsdev.github.io', 'sample-template_repository'];
/**
* Strip prefixes from topic names to get clean category names
* @param {string} topic - Topic with prefix (e.g., 'product-odm')
* @returns {string} Clean name (e.g., 'odm')
*/
function stripPrefix(topic) {
return topic.replace(/^(product|comp|type)-/, '');
}
/**
* Fetch all repositories from GitHub organization
* @returns {Promise<Array>} Array of repository objects
*/
function fetchRepositories() {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.github.com',
path: `/orgs/${ORG_NAME}/repos?per_page=100`,
method: 'GET',
headers: {
'User-Agent': 'DecisionsDev-Site',
'Accept': 'application/vnd.github.v3+json'
}
};
// Add authentication if token is available
if (GITHUB_TOKEN) {
options.headers['Authorization'] = `token ${GITHUB_TOKEN}`;
}
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode !== 200) {
reject(new Error(`GitHub API returned status ${res.statusCode}: ${data}`));
return;
}
try {
const repos = JSON.parse(data);
resolve(repos);
} catch (error) {
reject(new Error(`Failed to parse GitHub response: ${error.message}`));
}
});
});
req.on('error', (error) => {
reject(new Error(`Request failed: ${error.message}`));
});
req.end();
});
}
/**
* Process repositories and add categorization
* @param {Array} repos - Raw repository data from GitHub
* @returns {Array} Processed repositories with categories
*/
function processRepositories(repos) {
return repos
.filter(repo => !EXCLUDED_REPOS.includes(repo.name)) // Exclude repositories in EXCLUDED_REPOS list
.filter(repo => !repo.private) // Only include public repositories
.map(repo => {
// Analyze repository to get suggested categories
const analysis = analyzeRepository(repo);
// Build categories object with clean names
const categories = {
products: analysis.products.map(stripPrefix),
components: analysis.components.map(stripPrefix)
};
// Return repository with both original topics and new categories
return {
name: repo.name,
description: repo.description,
topics: repo.topics || [], // Preserve original GitHub topics
categories: categories, // Add structured categories with clean names
url: repo.html_url,
stars: repo.stargazers_count || 0, // Add star count
forks: repo.forks_count || 0, // Add fork count
updated_at: repo.updated_at // Add last updated date
};
});
}
/**
* Main execution
*/
async function main() {
try {
console.log('Fetching repositories from GitHub...');
const repos = await fetchRepositories();
console.log(`Found ${repos.length} repositories`);
console.log('Processing repositories and applying categorization...');
const processedRepos = processRepositories(repos);
// Sort repositories by name
processedRepos.sort((a, b) => a.name.localeCompare(b.name));
// Write to file
const outputDir = path.dirname(OUTPUT_PATH);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(processedRepos, null, 2));
console.log(`✓ Successfully wrote ${processedRepos.length} repositories to ${OUTPUT_PATH}`);
// Print summary statistics
const stats = {
withProducts: processedRepos.filter(r => r.categories.products.length > 0).length,
withComponents: processedRepos.filter(r => r.categories.components.length > 0).length,
withoutProducts: processedRepos.filter(r => r.categories.products.length === 0).length
};
console.log('\nCategorization Summary:');
console.log(` Repositories with products: ${stats.withProducts}`);
console.log(` Repositories with components: ${stats.withComponents}`);
console.log(` Repositories without products (will appear in "Other"): ${stats.withoutProducts}`);
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = { fetchRepositories, processRepositories, stripPrefix };
// Made with Bob