У меня есть сайт под названием nexttexteditor.com, он размещен на страницах Github и сгенерирован с помощью Jekyll (Github поддерживает генерацию Jekyll, как только вы отправляете новый код в Github, он генерирует ваш сайт Jekyll, часто в считанные секунды).

В любом случае, сайт довольно прост и состоит из списка фреймворков javascript для создания редакторов форматированного текста.

Информация о фреймворках хранится в YAML-файле и работает как база данных.

Информация в этом YAML-файле затем будет представлена ​​на веб-сайте. Итак, этот YAML-файл…

… Становится этим сайтом.

В этом нет ничего нового, просто так работает Джекил. Но если вы обратите внимание на скриншоты, вы увидите, что я представляю такую ​​информацию, как количество звезд на Github, количество открытых проблем и время последнего обновления репозитория. Как вы можете автоматически обновлять такие данные на статически сгенерированном сайте? Ответ запланирован (через Cloudwatch) AWS Lambda и Github API.

Лямбда-функция создается с помощью javascript в среде Node.js.

Процесс обновления данных следующий:

  1. Правило CloudWatch должно запускать лямбда-функцию каждые 12 часов.
  2. Функция загружает конкретный файл _data / editors.yml через Github API.
  3. Преобразуйте YML в JSON, чтобы упростить работу
  4. Перебрать все «редакторы» и получить информацию о каждом из репозиториев.
  5. Обновите файл editors.yml свежими значениями.
  6. Зафиксировать / отправить editors.yml в Github
  7. Github Pages замечает новую фиксацию и начинает создавать новый сайт с помощью Jekyll. Выполнено!

Лямбда-функция

Вы можете найти исходный код этой функции на Github.

let request = require('request');
const YAML = require('yamljs');

/*
 Required environment vars
 */
const userAgent = process.env['USER_AGENT']
const username = process.env['USERNAME']
const email = process.env['EMAIL']
const key = process.env['KEY']

const urlToUpdate = `https://${username}:${key}@api.github.com/repos/andene/texteditors/contents/_data/editors.yml`


/**
 * Takes a URL for a Github repository and remove the
 * first part containing github.com
 * @param url
 * @returns {string}
 */
const parseUrlForRepoName = (url) => {
    if (url.indexOf('https') < 0) {

    }
    const githubString = 'https://github.com'
    return url.substring(githubString.length, url.length)
}


/**
 * Helper method to get option object for a request
 * @param method
 * @param url
 * @returns {{url: *, method: string, headers: {User-Agent: *}}}
 */
const getOptionsForRequest = (method, url) => {
    return {
        url: url,
        method: method.toUpperCase(),
        headers: {
            'User-Agent': userAgent
        }
    }
}


/**
 * Return information for a Github repsository
 * Resolves with information parsed as JSON
 * @param url
 * @returns {Promise}
 */
const getRepoInformation = (url) => {

    return new Promise((resolve, reject) => {

        const options = getOptionsForRequest('get', url)
        request(options, function (error, response, body) {
            if (error) {
                reject(error)
                return
            }

            const repoData = JSON.parse(body)
            console.info(`Fetched:: ${repoData.name} (${repoData.url})`)
            resolve(repoData)
        })
    })
}


/**
 * Updates selected properties on editor object
 * @param githubInformation
 * @param editor
 */
const updateFile = (githubInformation, editor) => {
    editor.stargazers_count = githubInformation.stargazers_count
    editor.open_issues = githubInformation.open_issues
    editor.watchers = githubInformation.watchers
    editor.updated_at = githubInformation.updated_at
    editor.github_description = githubInformation.description
}


/**
 * 
 * @param editors
 * @param sha
 * @returns {Promise}
 */
const updateRepo = (editors, sha) => {

    return new Promise((resolve, reject) => {
        const yamlString = YAML.stringify(editors, 2)
        const base64Yaml = new Buffer(yamlString)

        const updateOptions = getOptionsForRequest('put', urlToUpdate)
        updateOptions.body = JSON.stringify({
            "message": "Updated editors.yml with Lambda",
            "committer": {
                "name": username,
                "email": email
            },
            "content": base64Yaml.toString('base64'),
            "sha": sha
        })

        request(updateOptions, function (error, response, body) {
            if (error) {
                reject(error)
                return
            }

            const parsedBody = JSON.parse(body)
            const result = `Done:: File is pushed with SHA: ${parsedBody.commit.sha} (${parsedBody.commit.html_url})`
            resolve(result)
        })
    })

}

const start = (event, context, callback) => { //Learn more about these lamba params at http://docs.aws.amazon.com/lambda/latest/dg/welcome.html

    const options = getOptionsForRequest('get', urlToUpdate)
    getRepoInformation(options.url)
        .then(repoData => {

            const fileContent = Buffer.from(repoData.content, 'base64')
            const editors = YAML.parse(String(fileContent))

            const repoFetches = editors.map(editor => {
                const repoPath = parseUrlForRepoName(editor.github)
                const repoUrl = `https://${username}:${key}@api.github.com/repos${repoPath}`
                return getRepoInformation(repoUrl)
                    .then(repoInformation => {
                        updateFile(repoInformation, editor)
                    })
            })

            Promise.all(repoFetches).then(() => {
                console.info(`All editors updated`)

                updateRepo(editors, repoData.sha).then(result => {
                    callback(null, result);
                }).catch(error => {
                    callback(error, null)
                })

            })
        })
}


if (process.env['LAMBDA_TASK_ROOT']) { // Running in AWS Lamba
    exports.handler = (event, context, callback) => {
        process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT']
        start(event, context, callback)
    }

} else { // Running in local environment
    start({}, {}, (error, result) => {
        console.info(result)
    })
}

Создайте лямбду

Если вы никогда раньше не создавали Lambda, я рекомендую официальное руководство по началу работы. Обычно я начинаю с Пустой функции.

Не забудьте добавить зависимости в zip-файл. В моем случае я использую YAML-парсер и запрос, чтобы упростить обработку HTTP-запросов.

Я просто заархивирую index.js вместе с node_modules.

Затем войдите в Консоль AWS, создайте свою функцию и загрузите свой код. Я также использую переменные среды для передачи имени пользователя Github и токена личного доступа функции.

Когда будете готовы, просто нажмите «Сохранить и протестировать».

Запускать лямбда-функцию каждые 12 часов

Используя CloudWatch, вы можете настроить правило, которое будет выполнять лямбда-функцию каждые двенадцать часов или любой другой интервал, который вы хотите.

Перейдите в CloudWatch, щелкните Правила и создайте новое правило. Источник события должен быть типа «Запланировано». При выборе этой опции вы можете выбрать фиксированную ставку или указать выражение cron.

Справа вы должны найти свою лямбда-функцию. Моя лямбда-функция в приведенном ниже примере называется github-downloader.

Получите токен личного доступа Github

Надеюсь, вы включили 2FA для своей учетной записи Github, после чего вам нужно будет сгенерировать личный токен доступа для своей учетной записи Github. Это позволит вам использовать Github API.

Перейдите на свою страницу настроек Персональные токены доступа (нижний левый угол)› Нажмите Сгенерировать новый токен

После этого вы увидите новый сгенерированный ключ, сохраните!

Резюме

Я очень доволен этим решением. Мне не нужно беспокоиться о том, что серверы будут установлены и настроены, а запуск AWS Lambda очень дешев, эта функция запускается примерно за 3 секунды.

Мне очень нравится запускать Jekyll на страницах Github, если у вас есть доступ к Интернету и компьютеру / мобильному устройству, вы можете обновлять свой сайт, неважно, где вы находитесь, или вы можете обновить его с помощью Lambda.

Обновление информации о репозиториях Github - это лишь одна из областей использования, я уверен, что вы можете найти другие области и API, где это может пригодиться.

Просто подсказка, есть много информации о репозиториях, доступных в Github API, например, посмотрите репозиторий aws-cli.

Ссылки