Skip to content

Commit f2abb36

Browse files
committed
Add check for org level community health files
1 parent cd8a3ed commit f2abb36

3 files changed

Lines changed: 124 additions & 13 deletions

File tree

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,26 +123,37 @@ node dist/index.js create --token your_github_token --name my-awesome-project --
123123

124124
## What Gets Evaluated
125125

126-
The CLI evaluates repositories based on these criteria:
126+
The CLI evaluates repositories based on these criteria. When community health files are missing from a repository, the tool automatically checks the organization's `.github` repository for default community health files:
127127

128128
### Essential Elements (High Weight)
129129
- **README File** (20 points): Comprehensive documentation
130130
- **Contributing Guidelines** (15 points): Clear contribution instructions
131131
- **Open Source License** (15 points): Valid open source license
132132

133133
### Community Building (Medium Weight)
134-
- **Code of Conduct** (12 points): Welcoming environment guidelines
134+
- **Code of Conduct** (12 points): Welcoming environment guidelines (checks repository and organization's `.github` repo)
135135
- **Description** (10 points): Clear project description
136136
- **Good First Issues** (10 points): Issues labeled for beginners
137137

138138
### Discoverability & Organization (Lower Weight)
139139
- **Topics/Tags** (8 points): Relevant repository topics
140-
- **Issue Templates** (8 points): Standardized issue reporting
140+
- **Issue Templates** (8 points): Standardized issue reporting (checks repository and organization's `.github` repo)
141141
- **Help Wanted Issues** (8 points): Issues seeking community help
142-
- **Pull Request Template** (7 points): PR submission guidelines
142+
- **Pull Request Template** (7 points): PR submission guidelines (checks repository and organization's `.github` repo)
143143
- **Repository Name** (5 points): Descriptive and relevant name
144144
- **Active Repository** (5 points): Not archived
145145

146+
### Organization-Level Community Health Files
147+
148+
RepoReady automatically checks for community health files in the organization's `.github` repository when they're missing from the individual repository. This follows [GitHub's community health file standards](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file) where organizations can provide default files for all their repositories.
149+
150+
Files checked at the organization level include:
151+
- `CODE_OF_CONDUCT.md`
152+
- `CONTRIBUTING.md`
153+
- `LICENSE` (and variants)
154+
- Issue templates in `.github/ISSUE_TEMPLATE/`
155+
- `PULL_REQUEST_TEMPLATE.md`
156+
146157
## Scoring & Ratings
147158

148159
- **Excellent** (90-100%): Ready for contributors with outstanding setup

src/evaluator/criteria.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,19 @@ export function generateRecommendations(results: Array<{ criteria: string; passe
129129
recommendations.push('Create a comprehensive README.md with project description, installation, and usage instructions');
130130
break;
131131
case 'Contributing Guidelines':
132-
recommendations.push('Add a CONTRIBUTING.md file explaining how others can contribute to your project');
132+
recommendations.push('Add a CONTRIBUTING.md file explaining how others can contribute to your project (can be added to the repository or organization\'s .github repository)');
133133
break;
134134
case 'Code of Conduct':
135-
recommendations.push('Add a CODE_OF_CONDUCT.md file to create a welcoming environment for contributors');
135+
recommendations.push('Add a CODE_OF_CONDUCT.md file to create a welcoming environment for contributors (can be added to the repository or organization\'s .github repository)');
136136
break;
137137
case 'Open Source License':
138-
recommendations.push('Add an open source license (MIT, Apache 2.0, GPL, etc.) to make your project truly open source');
138+
recommendations.push('Add an open source license (MIT, Apache 2.0, GPL, etc.) to make your project truly open source (can be added to the repository or organization\'s .github repository)');
139139
break;
140140
case 'Issue Templates':
141-
recommendations.push('Create issue templates in .github/ISSUE_TEMPLATE/ to help contributors report bugs and request features');
141+
recommendations.push('Create issue templates in .github/ISSUE_TEMPLATE/ to help contributors report bugs and request features (can be added to the repository or organization\'s .github repository)');
142142
break;
143143
case 'Pull Request Template':
144-
recommendations.push('Add a pull request template to standardize contribution submissions');
144+
recommendations.push('Add a pull request template to standardize contribution submissions (can be added to the repository or organization\'s .github repository)');
145145
break;
146146
case 'Good First Issues':
147147
recommendations.push('Create and label some issues as "good first issue" to welcome new contributors');

src/utils/github.ts

Lines changed: 104 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ export class GitHubService {
8989
return true;
9090
}
9191
}
92-
return false;
92+
93+
// Check organization's .github repository for community health files
94+
return await this.checkOrgCommunityFile(owner, 'CONTRIBUTING.md');
9395
}
9496

9597
private async checkCodeOfConductFile(owner: string, repo: string): Promise<boolean> {
@@ -105,7 +107,9 @@ export class GitHubService {
105107
return true;
106108
}
107109
}
108-
return false;
110+
111+
// Check organization's .github repository for community health files
112+
return await this.checkOrgCommunityFile(owner, 'CODE_OF_CONDUCT.md');
109113
}
110114

111115
private async checkLicenseFile(owner: string, repo: string): Promise<boolean> {
@@ -124,6 +128,15 @@ export class GitHubService {
124128
return true;
125129
}
126130
}
131+
132+
// Check organization's .github repository for license file
133+
const licenseFiles = ['LICENSE', 'LICENSE.md', 'LICENSE.txt'];
134+
for (const filename of licenseFiles) {
135+
if (await this.checkOrgCommunityFile(owner, filename)) {
136+
return true;
137+
}
138+
}
139+
127140
return false;
128141
}
129142

@@ -149,7 +162,9 @@ export class GitHubService {
149162
return true;
150163
}
151164
}
152-
return false;
165+
166+
// Check organization's .github repository for issue templates
167+
return await this.checkOrgIssueTemplates(owner);
153168
}
154169
}
155170

@@ -166,7 +181,9 @@ export class GitHubService {
166181
return true;
167182
}
168183
}
169-
return false;
184+
185+
// Check organization's .github repository for PR template
186+
return await this.checkOrgCommunityFile(owner, 'PULL_REQUEST_TEMPLATE.md');
170187
}
171188

172189
private async checkLabeledIssues(owner: string, repo: string): Promise<{ hasGoodFirst: boolean; hasHelpWanted: boolean }> {
@@ -215,4 +232,87 @@ export class GitHubService {
215232
throw new Error(`Failed to create repository: ${error}`);
216233
}
217234
}
235+
236+
/**
237+
* Check if a community health file exists in the organization's .github repository
238+
* @param owner The organization name
239+
* @param filename The community health file to check for (e.g., 'CODE_OF_CONDUCT.md')
240+
* @returns Promise<boolean> indicating if the file exists
241+
*/
242+
private async checkOrgCommunityFile(owner: string, filename: string): Promise<boolean> {
243+
try {
244+
// First check if the .github repository exists for this organization
245+
await this.octokit.repos.get({
246+
owner,
247+
repo: '.github',
248+
});
249+
250+
// Check for the file in various possible locations within the org's .github repo
251+
const possiblePaths = [
252+
`profile/${filename}`,
253+
filename,
254+
`.github/${filename}`
255+
];
256+
257+
for (const path of possiblePaths) {
258+
if (await this.checkFileExists(owner, '.github', path)) {
259+
return true;
260+
}
261+
}
262+
263+
return false;
264+
} catch {
265+
// If .github repository doesn't exist or isn't accessible, return false
266+
return false;
267+
}
268+
}
269+
270+
/**
271+
* Check if issue templates exist in the organization's .github repository
272+
* @param owner The organization name
273+
* @returns Promise<boolean> indicating if issue templates exist
274+
*/
275+
private async checkOrgIssueTemplates(owner: string): Promise<boolean> {
276+
try {
277+
// First check if the .github repository exists for this organization
278+
await this.octokit.repos.get({
279+
owner,
280+
repo: '.github',
281+
});
282+
283+
// Check for issue templates in the org's .github repo
284+
const possiblePaths = [
285+
'.github/ISSUE_TEMPLATE',
286+
'ISSUE_TEMPLATE',
287+
'.github/ISSUE_TEMPLATE.md',
288+
'ISSUE_TEMPLATE.md'
289+
];
290+
291+
for (const path of possiblePaths) {
292+
try {
293+
const { data } = await this.octokit.repos.getContent({
294+
owner,
295+
repo: '.github',
296+
path,
297+
});
298+
299+
// If it's a directory with templates, check if it has content
300+
if (Array.isArray(data) && data.length > 0) {
301+
return true;
302+
}
303+
// If it's a single template file
304+
if (!Array.isArray(data)) {
305+
return true;
306+
}
307+
} catch {
308+
// Continue checking other paths
309+
}
310+
}
311+
312+
return false;
313+
} catch {
314+
// If .github repository doesn't exist or isn't accessible, return false
315+
return false;
316+
}
317+
}
218318
}

0 commit comments

Comments
 (0)