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
name: Update Progit Book | ||
on: | ||
- workflow_dispatch | ||
permissions: | ||
contents: write # need to be able to push | ||
jobs: | ||
check-for-updates: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
const getFileContents = async (path) => { | ||
const req = await fetch(`https://github.com/${context.repo.owner}/${context.repo.repo}/raw/${context.sha}/${path}`) | ||
if (req.status === 404) return undefined | ||
return req.text() | ||
} | ||
const getAllBooks = async () => { | ||
const book_rb = await getFileContents('script/book.rb') | ||
const begin = book_rb.indexOf('@@all_books = {') | ||
const end = book_rb.indexOf('}', begin + 1) | ||
if (begin < 0 || end < 0) throw new Error(`Could not find @@all_books in:\n${book_rb}`) | ||
return book_rb | ||
.substring(begin, end) | ||
.split('\n') | ||
.reduce((allBooks, line) => { | ||
const match = line.match(/"([^"]+)" => "([^"]+)"/) | ||
if (match) allBooks[match[1]] = match[2] | ||
return allBooks | ||
}, {}) | ||
return allBooks | ||
} | ||
// This is a very, very simplistic YAML parser, _just_ powerful enough to | ||
// parse the front matter written by us. | ||
// | ||
// Most importantly, support for continuation lines (lines ending in `|`) is | ||
// completely absent. Also, support for JSON-style definitions is missing. | ||
const parseFrontMatter = page => { | ||
if (!page.startsWith('---\n')) throw new Error(`Expected ---:\n${page.substring(0, 20)}...`) | ||
const end = page.indexOf('\n\n---', 4) | ||
if (end < 0) throw new Error(`Missing end of front matter:\n${page}`) | ||
const stack = [{}] | ||
const keys = [] | ||
return page | ||
.substring(4, end) | ||
.split('\n') | ||
.reduce((frontMatter, line) => { | ||
// parse these three line formats: | ||
// - value | ||
// key: value | ||
// key: | ||
const match = line.match(/^( *)(?:(?:(-)\s*|(\S+):)\s*("?(\S.*?)"?)?)$/) | ||
if (!match) throw new Error(`Invalid YAML? '${line}'`) | ||
const isArrayItem = match[2] | ||
const key = match[3] | ||
const hasValue = match[4] | ||
const value = match[5] | ||
const nestingLevel = match[1].length / 2 + (isArrayItem ? 1 : 0) | ||
if (nestingLevel + 1 < stack.length) stack.splice(nestingLevel + 1, stack.length - nestingLevel - 1) | ||
else if (nestingLevel >= stack.length) throw new Error(`Invalid nesting level:\n${line}`) | ||
const o = stack[nestingLevel] | ||
if (isArrayItem) { | ||
if (Array.isArray(o)) o.push(value) | ||
else if (typeof o !== 'object') throw new Error(`Could not turn parent into an array:\n${line}`) | ||
else if (Object.keys(o).length > 0) throw new Error(`Trying to add an array item to an object:\n${line}\n${JSON.stringify(o, null, 2)}`) | ||
else { | ||
stack[nestingLevel - 1][keys[nestingLevel - 1]] = stack[nestingLevel] = [value] | ||
} | ||
} | ||
else if (hasValue) o[key] = value | ||
else { | ||
o[key] = {} | ||
stack.push(o[key]) | ||
keys[nestingLevel] = key | ||
} | ||
return frontMatter | ||
}, stack[0]) | ||
} | ||
const getFrontMatterForLanguage = async (lang) => { | ||
const frontPage = await getFileContents(`book/${lang}/v2/index.html`) | ||
return parseFrontMatter(frontPage) | ||
} | ||
update- | ||