Skip to content

Commit 86af117

Browse files
authored
add files
after run test
1 parent 8503def commit 86af117

File tree

7 files changed

+260
-1
lines changed

7 files changed

+260
-1
lines changed

Gemfile.lock

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ GEM
2525
ethon (0.12.0)
2626
ffi (>= 1.3.0)
2727
eventmachine (1.2.7)
28+
eventmachine (1.2.7-x64-mingw32)
2829
execjs (2.7.0)
2930
faraday (1.0.0)
3031
multipart-post (>= 1.2, < 3)
3132
ffi (1.12.1)
33+
ffi (1.12.1-x64-mingw32)
3234
forwardable-extended (2.6.0)
3335
gemoji (3.0.1)
3436
github-pages (204)
@@ -214,6 +216,8 @@ GEM
214216
multipart-post (2.1.1)
215217
nokogiri (1.10.7)
216218
mini_portile2 (~> 2.4.0)
219+
nokogiri (1.10.7-x64-mingw32)
220+
mini_portile2 (~> 2.4.0)
217221
nokogumbo (2.0.2)
218222
nokogiri (~> 1.8, >= 1.8.4)
219223
octokit (4.15.0)
@@ -254,11 +258,12 @@ GEM
254258

255259
PLATFORMS
256260
ruby
261+
x64-mingw32
257262

258263
DEPENDENCIES
259264
github-pages
260265
html-proofer
261266
rake
262267

263268
BUNDLED WITH
264-
2.1.2
269+
2.1.4

script/_site/bootstrap

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
cd "$(dirname "$0")/.."
6+
7+
echo "==> Installing gem dependencies…"
8+
bundle check --path vendor/gems &>/dev/null || {
9+
time bundle install --binstubs bin --path vendor/gems
10+
}
11+
12+
echo "==> Installing node dependencies…"
13+
npm install

script/_site/build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
echo "==> Building the site…"
3+
bundle exec jekyll build $@

script/_site/ebooks

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#!/usr/bin/env node
2+
3+
// Generate EPUB and MOBI ebooks
4+
5+
const fs = require('fs');
6+
const path = require('path');
7+
const exec = require('child_process').exec;
8+
const fastmatter = require('fastmatter');
9+
const mkdirp = require('mkdirp');
10+
const slug = require('slug');
11+
const unified = require('unified');
12+
const parse = require('remark-parse');
13+
const stringify = require('remark-stringify');
14+
const visit = require('unist-util-visit');
15+
const download = require('download');
16+
const ebrew = require('ebrew');
17+
18+
const processor = unified()
19+
.use(parse)
20+
.use(replaceAsides)
21+
.use(replaceCheckboxes)
22+
.use(fixImages)
23+
.use(downloadImages)
24+
.use(stringify);
25+
26+
buildBooks([
27+
'_articles/how-to-contribute.md',
28+
'_articles/starting-a-project.md',
29+
'_articles/finding-users.md',
30+
'_articles/building-community.md',
31+
'_articles/best-practices.md',
32+
'_articles/leadership-and-governance.md',
33+
'_articles/getting-paid.md',
34+
'_articles/code-of-conduct.md',
35+
'_articles/metrics.md',
36+
'_articles/legal.md',
37+
]);
38+
39+
function buildBooks(files) {
40+
const out = 'assets/ebooks/open-source-guide';
41+
const tempFile = '_ebook.md';
42+
43+
mkdirp.sync('assets/ebooks');
44+
mkdirp.sync('.asset-downloads');
45+
46+
let source = files.map(readFile).join('\n\n');
47+
const { text, promises } = downloadImages(source);
48+
fs.writeFileSync(tempFile, text);
49+
50+
promises
51+
.then(() => markdownToEpub(tempFile, `${out}.epub`))
52+
.then(() => epubToMobi(`${out}.epub`, `${out}.mobi`))
53+
.then(() => fs.unlinkSync(tempFile))
54+
.catch(err => console.error(err));
55+
}
56+
57+
function readFile(file) {
58+
const md = fs.readFileSync(file, 'utf8');
59+
60+
// Replace frontmatter with chapter title
61+
const { attributes, body } = fastmatter(md);
62+
let text = `# ${attributes.title}\n\n${body}`;
63+
64+
return processor.process(text).contents;
65+
}
66+
67+
function markdownToEpub(mdFile, epubFile) {
68+
return new Promise((resolve, reject) => {
69+
console.log(`Building ${epubFile}...`);
70+
71+
const manifestFile = '_ebook.json';
72+
fs.writeFileSync(manifestFile, JSON.stringify({
73+
tocDepth: 1,
74+
title: 'Open Source Guides',
75+
subtitle: 'Open source software is made by people just like you. Learn how to launch and grow your project.',
76+
author: 'GitHub',
77+
publisher: 'GitHub',
78+
rights: 'CC-BY-4.0',
79+
date: (new Date()).toISOString().slice(0, 10),
80+
contents: path.resolve(mdFile),
81+
}));
82+
83+
ebrew.generate(manifestFile, epubFile, err => {
84+
fs.unlinkSync(manifestFile);
85+
if (err) {
86+
reject(err.stack || err);
87+
} else {
88+
resolve();
89+
}
90+
});
91+
});
92+
}
93+
94+
function epubToMobi(epubFile, mobiFile) {
95+
return new Promise((resolve, reject) => {
96+
console.log(`Building ${mobiFile}...`);
97+
98+
const kindlegen = require.resolve('kindlegen/bin/kindlegen');
99+
exec(`${kindlegen} ${epubFile} -c2 -verbose -o ${path.basename(mobiFile)}`, (err, stdout) => {
100+
if (err && stdout.includes('MOBI file could not be generated because of errors!')) {
101+
reject(stdout);
102+
} else {
103+
resolve();
104+
}
105+
});
106+
});
107+
}
108+
109+
function downloadImages(text) {
110+
function image(processor) {
111+
return ast => visit(ast, 'image', node => {
112+
if (node.url.startsWith('http')) {
113+
const url = node.url;
114+
const filename = slug(path.basename(node.url));
115+
const filepath = `./.asset-downloads/${filename}.jpg`
116+
node.url = filepath;
117+
if (fs.existsSync(filepath)) {
118+
promises.push(Promise.resolve());
119+
} else {
120+
const promise = download(url)
121+
.then(data => {
122+
fs.writeFileSync(filepath, data);
123+
});
124+
promises.push(promise);
125+
}
126+
}
127+
});
128+
}
129+
130+
let promises = [];
131+
const processor = unified()
132+
.use(parse)
133+
.use(image)
134+
.use(stringify);
135+
136+
text = processor.process(text).contents;
137+
138+
return {
139+
promises: Promise.all(promises),
140+
text,
141+
};
142+
}
143+
144+
/*
145+
Replace asides with Markdown blockquotes:
146+
<aside markdown="1" class="pquote">
147+
<img src="https://avatars2.githubusercontent.com/u/1976330?v=3&s=460" class="pquote-avatar" alt="avatar" alt="@lord avatar">
148+
I fumbled it. I didn't put in the effort to come up with a complete solution. Instead of an half-assed solution, I wish I had said "I don't have time for this right now, but I'll add it to the long term nice-to-have list."
149+
<p markdown="1" class="pquote-credit">
150+
— @lord, ["Tips for new open source maintainers"](https://lord.io/blog/2014/oss-tips/)
151+
</p>
152+
</aside>
153+
*/
154+
function replaceAsides(processor) {
155+
return ast => visit(ast, 'html', node => {
156+
if (node.value.startsWith('<aside')) {
157+
node.value = node.value
158+
.replace(/ <img src="(.*?)"[^>]*>/, '![]($1)\n')
159+
.replace(/\(\/assets\//, '(./assets/')
160+
.replace(/<\/?(aside|p).*?>/g, '')
161+
.trim()
162+
.replace(/(^|\n) */g, '\n> ');
163+
}
164+
});
165+
}
166+
167+
/*
168+
Replace checkboxes with unordered lists:
169+
<div class="clearfix mb-2">
170+
<input type="checkbox" id="cbox1" class="d-block float-left mt-1 mr-2" value="checkbox">
171+
<label for="cbox1" class="overflow-hidden d-block text-normal">
172+
Does it have a license? Usually, this is a file called LICENSE in the root of the repository.
173+
</label>
174+
</div>
175+
*/
176+
function replaceCheckboxes(processor) {
177+
return ast => visit(ast, 'html', node => {
178+
if (node.value.startsWith('<div')) {
179+
node.value = node.value
180+
.replace(/<\/?.*?>/g, '')
181+
.trim()
182+
.replace(/^/, '* ');
183+
}
184+
});
185+
}
186+
187+
/*
188+
1. Remove image alts, otherwise they will be visible in a book
189+
2. Fix asset paths
190+
*/
191+
function fixImages(processor) {
192+
return ast => visit(ast, 'image', node => {
193+
node.alt = null;
194+
node.url = node.url.replace(/^\/assets\//, './assets/');
195+
});
196+
}

script/_site/html-proofer

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env ruby
2+
3+
require "bundler/setup"
4+
require "html-proofer"
5+
6+
url_ignores = [
7+
"https://okdistribute.xyz/post/okf-de",
8+
"https://www.drupal.org/community-initiatives/drupal-core/usability",
9+
"https://scripts.sil.org/ofl",
10+
]
11+
12+
HTMLProofer::Runner.new(
13+
["_site"],
14+
parallel: { in_threads: 4 },
15+
type: :directory,
16+
url_ignore: url_ignores,
17+
check_html: true,
18+
check_opengraph: true,
19+
check_img_http: true,
20+
favicon: true,
21+
assume_extension: true,
22+
only_4xx: true,
23+
).run

script/_site/server

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
bundle exec jekyll serve --watch --incremental --baseurl ''

script/_site/test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
script/build --config _config.yml,test/_config.yml
6+
bundle exec rake
7+
8+
set +e
9+
10+
script/html-proofer
11+
HTML_PROOFER_EXIT="$?"
12+
test/prose
13+
PROSE_EXIT="$?"
14+
[[ "$HTML_PROOFER_EXIT" == 0 && "$PROSE_EXIT" == 0 ]]

0 commit comments

Comments
 (0)