forked from WordPress/block-development-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Contributors and Creation Modification time of examples
- Loading branch information
1 parent
461ff94
commit a514369
Showing
9 changed files
with
559 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: Generate Contributors JSON | ||
|
||
on: | ||
workflow_dispatch: | ||
|
||
jobs: | ||
github-contributors: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: '20' | ||
|
||
- name: Generate Contributors JSON | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
run: node .github/workflows/scripts/github-contributors.js | ||
|
||
- name: Commit and Push Changes | ||
run: | | ||
git config --local user.email "action@github.com" | ||
git config --local user.name "GitHub Action" | ||
git add _data/contributors.json | ||
git commit -m "Update contributors.json" || echo "No changes to commit" | ||
git push |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
const fs = require( 'fs' ); | ||
const https = require( 'https' ); | ||
|
||
// Read examples.json | ||
const examples = JSON.parse( fs.readFileSync( '_data/examples.json', 'utf8' ) ); | ||
|
||
// Read existing contributors.json if it exists | ||
let existingContributors = []; | ||
try { | ||
existingContributors = JSON.parse( | ||
fs.readFileSync( '_data/contributors.json', 'utf8' ) | ||
); | ||
} catch ( e ) { | ||
// File doesn't exist yet, start with empty array | ||
existingContributors = []; | ||
} | ||
|
||
// Extract unique contributor emails, excluding ones we already have | ||
const existingEmails = new Set( existingContributors.map( ( c ) => c.mail ) ); | ||
const uniqueEmails = [ | ||
...new Set( examples.flatMap( ( example ) => example.contributors ) ), | ||
].filter( ( email ) => ! existingEmails.has( email ) ); | ||
|
||
// Function to get GitHub user info | ||
async function getGithubInfo( email ) { | ||
return new Promise( ( resolve ) => { | ||
const options = { | ||
hostname: 'api.github.com', | ||
path: `/search/users?q=${ encodeURIComponent( email ) }+in:email`, | ||
headers: { | ||
'User-Agent': 'Node.js', | ||
Authorization: `Bearer ${ process.env.GITHUB_TOKEN }`, | ||
}, | ||
}; | ||
|
||
https | ||
.get( options, ( res ) => { | ||
let data = ''; | ||
res.on( 'data', ( chunk ) => ( data += chunk ) ); | ||
res.on( 'end', () => { | ||
try { | ||
const result = JSON.parse( data ); | ||
if ( result.items && result.items.length > 0 ) { | ||
const user = result.items[ 0 ]; | ||
resolve( { | ||
mail: email, | ||
github: user.login, | ||
name: user.name || user.login, | ||
} ); | ||
} else { | ||
resolve( { | ||
mail: email, | ||
github: null, | ||
name: null, | ||
} ); | ||
} | ||
} catch ( e ) { | ||
resolve( { | ||
mail: email, | ||
github: null, | ||
name: null, | ||
} ); | ||
} | ||
} ); | ||
} ) | ||
.on( 'error', () => { | ||
resolve( { | ||
mail: email, | ||
github: null, | ||
name: null, | ||
} ); | ||
} ); | ||
} ); | ||
} | ||
|
||
// Process all new emails | ||
async function generateContributors() { | ||
const newContributors = []; | ||
for ( const email of uniqueEmails ) { | ||
const info = await getGithubInfo( email ); | ||
newContributors.push( info ); | ||
// Respect GitHub API rate limiting | ||
await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); | ||
} | ||
|
||
// Combine existing and new contributors | ||
const allContributors = [ ...existingContributors, ...newContributors ]; | ||
|
||
// Sort by email to maintain consistent ordering | ||
allContributors.sort( ( a, b ) => a.mail.localeCompare( b.mail ) ); | ||
|
||
return allContributors; | ||
} | ||
|
||
// Generate and save the file | ||
generateContributors().then( ( contributors ) => { | ||
fs.writeFileSync( | ||
'_data/contributors.json', | ||
JSON.stringify( contributors, null, 2 ) | ||
); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { execSync } from 'child_process'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { Example } from '../types/example'; | ||
import { withErrorHandling } from '../utils/compose'; | ||
import { readJsonFile, writeFile } from '../utils/fileOperations'; | ||
import { PLUGINS_PATH, EXAMPLES_DATA_PATH } from '../constants'; | ||
|
||
export class ContributorsService { | ||
private examples: Example[]; | ||
|
||
constructor() { | ||
this.examples = []; | ||
} | ||
|
||
private async loadExamples(): Promise< void > { | ||
this.examples = await readJsonFile( EXAMPLES_DATA_PATH ); | ||
} | ||
|
||
private getContributors( pluginPath: string ): string[] { | ||
const gitCommand = `git log --format='%ae' -- ${ pluginPath } | sort -u`; | ||
const output = execSync( gitCommand, { encoding: 'utf8' } ); | ||
|
||
return output | ||
.split( '\n' ) | ||
.filter( Boolean ) | ||
.map( ( email ) => email.replace( /'/g, '' ) ); | ||
} | ||
|
||
private async processExample( example: Example ): Promise< void > { | ||
const pluginPath = path.join( PLUGINS_PATH, example.slug ); | ||
|
||
if ( fs.existsSync( pluginPath ) ) { | ||
try { | ||
example.contributors = this.getContributors( pluginPath ); | ||
} catch ( error ) { | ||
console.error( `Error processing ${ example.slug }:`, error ); | ||
} | ||
} | ||
} | ||
|
||
private async saveExamples(): Promise< void > { | ||
await writeFile( | ||
EXAMPLES_DATA_PATH, | ||
JSON.stringify( this.examples, null, '\t' ) + '\n' | ||
); | ||
} | ||
|
||
public async updateContributors(): Promise< void > { | ||
await this.loadExamples(); | ||
|
||
for ( const example of this.examples ) { | ||
await this.processExample( example ); | ||
} | ||
|
||
await this.saveExamples(); | ||
} | ||
} | ||
|
||
// Create error-handled version of the service methods | ||
export const contributorsService = { | ||
updateContributors: withErrorHandling( async () => { | ||
const service = new ContributorsService(); | ||
await service.updateContributors(); | ||
} ), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { execSync } from 'child_process'; | ||
import * as path from 'path'; | ||
import { Example } from '../types/example'; | ||
import { withErrorHandling } from '../utils/compose'; | ||
import { readJsonFile, writeFile } from '../utils/fileOperations'; | ||
import { PLUGINS_PATH, EXAMPLES_DATA_PATH } from '../constants'; | ||
|
||
export class DatesService { | ||
private examples: Example[]; | ||
|
||
constructor() { | ||
this.examples = []; | ||
} | ||
|
||
private async loadExamples(): Promise< void > { | ||
this.examples = await readJsonFile( EXAMPLES_DATA_PATH ); | ||
} | ||
|
||
private getGitDates( pluginPath: string ): { | ||
created: string; | ||
lastModified: string; | ||
} { | ||
// Get first commit date | ||
const createdCommand = `git log --reverse --format=%aI -- ${ pluginPath } | head -n 1`; | ||
const created = execSync( createdCommand, { | ||
encoding: 'utf8', | ||
} ).trim(); | ||
|
||
// Get last commit date | ||
const lastModifiedCommand = `git log -1 --format=%aI -- ${ pluginPath }`; | ||
const lastModified = execSync( lastModifiedCommand, { | ||
encoding: 'utf8', | ||
} ).trim(); | ||
|
||
return { | ||
created, | ||
lastModified, | ||
}; | ||
} | ||
|
||
private async processExample( example: Example ): Promise< void > { | ||
const pluginPath = path.join( PLUGINS_PATH, example.slug ); | ||
|
||
try { | ||
const dates = this.getGitDates( pluginPath ); | ||
example.created = dates.created; | ||
example.lastModified = dates.lastModified; | ||
} catch ( error ) { | ||
console.error( | ||
`Error processing dates for ${ example.slug }:`, | ||
error | ||
); | ||
} | ||
} | ||
|
||
private async saveExamples(): Promise< void > { | ||
await writeFile( | ||
EXAMPLES_DATA_PATH, | ||
JSON.stringify( this.examples, null, '\t' ) + '\n' | ||
); | ||
} | ||
|
||
public async updateDates(): Promise< void > { | ||
await this.loadExamples(); | ||
|
||
for ( const example of this.examples ) { | ||
await this.processExample( example ); | ||
} | ||
|
||
await this.saveExamples(); | ||
} | ||
} | ||
|
||
// Create error-handled version of the service methods | ||
export const datesService = { | ||
updateDates: withErrorHandling( async () => { | ||
const service = new DatesService(); | ||
await service.updateDates(); | ||
} ), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es2018", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"sourceMap": true, | ||
"target": "ES2020", | ||
"module": "CommonJS", | ||
"outDir": "./dist", | ||
"rootDir": ".", | ||
"rootDir": "./", | ||
"strict": true, | ||
"esModuleInterop": true, | ||
"skipLibCheck": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"resolveJsonModule": true, | ||
"allowJs": true, | ||
"declaration": true, | ||
"lib": [ "ES2021" ] | ||
"forceConsistentCasingInFileNames": true | ||
}, | ||
"exclude": [ "node_modules" ] | ||
"include": [ "./**/*.ts" ], | ||
"exclude": [ "node_modules", "dist" ] | ||
} |
Oops, something went wrong.