Skip to content

Instantly share code, notes, and snippets.

@halex2005
Created November 24, 2017 20:54
Show Gist options
  • Save halex2005/245b429277b707c2fed83dcd2a3f0ca6 to your computer and use it in GitHub Desktop.
Save halex2005/245b429277b707c2fed83dcd2a3f0ca6 to your computer and use it in GitHub Desktop.
Automate github issue creation for Jekyll-based blog
{
"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"
}
}
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