Created
November 24, 2017 20:54
-
-
Save halex2005/245b429277b707c2fed83dcd2a3f0ca6 to your computer and use it in GitHub Desktop.
Automate github issue creation for Jekyll-based blog
This file contains hidden or 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
{ | |
"scripts": { | |
"patch-posts": "node -r babel-core/register patch-posts.js" | |
}, | |
"author": "halex2005", | |
"license": "MIT", | |
"devDependencies": { | |
"babel-core": "^6.26.0", | |
"babel-preset-es2015": "^6.24.1", | |
"bluebird": "^3.5.1", | |
"gray-matter": "^3.1.1", | |
"js-yaml": "^3.10.0", | |
"octokat": "^0.9.2", | |
"promptly": "^2.2.0" | |
} | |
} |
This file contains hidden or 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
import bluebird from 'bluebird'; | |
import Octokat from 'octokat'; | |
import promptly from 'promptly'; | |
import nodeFS from 'fs'; | |
import os from 'os'; | |
import path from 'path'; | |
import matter from 'gray-matter'; | |
import yaml from 'js-yaml'; | |
const fs = bluebird.promisifyAll(nodeFS); | |
const jekyllConfig = yaml.safeLoad(fs.readFileSync('_config.yml')); | |
async function GetCredentials() { | |
const username = await promptly.prompt('GitHub username: '); | |
const password = await promptly.password('Password: ', { replace: '*' }); | |
const owner = await promptly.prompt('Owner: '); | |
const repository = await promptly.prompt('Repository: '); | |
return { username, password, owner, repository }; | |
} | |
async function extractFrontmatter(postPath) { | |
const content = await fs.readFileAsync(postPath); | |
const frontmatter = matter(content); | |
return { | |
postPath, | |
issueName: path.parse(postPath).name, | |
frontmatter, | |
frontmatterChanged: false, | |
needComments() { | |
return this.frontmatter.data.comments === true; | |
}, | |
getGithubIssueId() { | |
return this.frontmatter.data.github_issue_id; | |
}, | |
setGithubIssueId(github_issue_id) { | |
this.frontmatter.data.github_issue_id = github_issue_id; | |
this.frontmatterChanged = true; | |
}, | |
getTags() { | |
return [...new Set(this.frontmatter.data.tags)]; | |
} | |
}; | |
} | |
async function createGithubIssueForPost(postWithFrontmatter, repo) { | |
const parsedName = postWithFrontmatter.issueName.split('-') | |
const year = parsedName[0]; | |
const issueNameWithoutDate = parsedName.slice(3).join('-'); | |
const issueData = { | |
title: `${postWithFrontmatter.issueName}`, | |
body: `Original post: [${postWithFrontmatter.frontmatter.data.title}](${jekyllConfig.url}/${year}/${issueNameWithoutDate}/)`, | |
labels: postWithFrontmatter.getTags() | |
}; | |
try { | |
const createdIssue = await repo.issues.create(issueData); | |
postWithFrontmatter.setGithubIssueId(createdIssue.number); | |
console.log(`issue #${createdIssue.number} created for post ${createdIssue.title}`) | |
try { | |
await repo.issues(createdIssue.number).update({ state: 'closed' }); | |
} | |
catch (error) { | |
console.log(`Error while closing issue #${createdIssue.number}: `, error); | |
} | |
} | |
catch (error) { | |
console.log(`Failed to create issue for post ${JSON.stringify(issueData)}`); | |
console.log(error); | |
} | |
} | |
async function WriteFrontmatterToDiskIfChanged(postWithFrontmatter) { | |
if (!postWithFrontmatter.frontmatterChanged) { | |
return; | |
} | |
await fs.writeFileAsync( | |
postWithFrontmatter.postPath, | |
matter.stringify( | |
postWithFrontmatter.frontmatter.content, | |
postWithFrontmatter.frontmatter.data)); | |
} | |
const credentials = GetCredentials() | |
.then(async credentials => { | |
const repo = new Octokat(credentials) | |
.repos(credentials.owner, credentials.repository); | |
const concurrencyOptions = { concurrency: os.cpus().length }; | |
const postsPath = path.join(__dirname, '../_posts'); | |
const posts = (await fs.readdirAsync(postsPath)) | |
.map(post => path.join(postsPath, post)) | |
.sort(); | |
const frontmatters = await bluebird.map( | |
posts, | |
extractFrontmatter, | |
concurrencyOptions); | |
const existingIssues = await repo.issues.fetchAll({ state: 'all' }); | |
const postsToCreateNewIssue = frontmatters | |
.filter(root => root.needComments() && !root.getGithubIssueId()) | |
.filter(root => { | |
const foundIssue = existingIssues.find(issue => issue.title === root.issueName); | |
if (!foundIssue) { | |
return true; | |
} | |
root.setGithubIssueId(foundIssue.number); | |
return false; | |
}); | |
if (postsToCreateNewIssue.length) { | |
await bluebird.mapSeries( | |
postsToCreateNewIssue, | |
root => createGithubIssueForPost(root, repo), | |
concurrencyOptions); | |
} else { | |
console.log("No new issues will be created"); | |
} | |
await bluebird.map( | |
frontmatters, | |
WriteFrontmatterToDiskIfChanged, | |
concurrencyOptions); | |
const postsToCheck = frontmatters.filter(root => root.needComments() && root.getGithubIssueId()) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment