Как разработчик, лучший способ продемонстрировать свой талант - это веб-сайт-портфолио. Многие из нас хотят создать «идеальное» веб-портфолио, которое будет эстетично выглядеть, а также продемонстрировать наши навыки и эффективную работу. Но как только мы создаем наше веб-портфолио, мы часто забываем поддерживать его в актуальном состоянии. У большинства из нас даже есть резюме, которое устарело уже много лет. Это происходит потому, что в нашей занятой жизни мы не хотим тратить дополнительное время на обновление наших резюме / портфолио.

В мире ботов и автоматизации я подумал, почему я не могу придумать для себя веб-сайт, который автоматически обновляется бесплатно. Конечная цель - это волшебный веб-сайт, который каким-то образом постоянно обновляется с помощью последних проектов, которые мы делаем, и это должно происходить без ручного вмешательства владельцев.

Живой пример можно увидеть здесь: http://atulr.com

Прежде чем мы перейдем к части автоматизации, давайте сначала разберемся с основами.

Github как хост

Когда мы думаем о создании веб-сайта-портфолио, первое, что приходит в голову, - это где его разместить? Войдите в Github; Большинство из вас используют Github для размещения своих проектов с открытым исходным кодом. Помимо предоставления бесплатного хостинга для проектов. Github также предоставляет бесплатный сервис под названием Github Pages. Страницы Github можно использовать для размещения веб-сайта вашего портфолио или даже отдельных веб-страниц для ваших проектов. Чтобы узнать, как настроить страницы github, посетите: https://pages.github.com/

Таким образом, Github теперь становится платформой для размещения наших проектов и нашего веб-сайта портфолио.

Создание сайта

Следующим шагом будет создание нашего веб-сайта-портфолио. Базовый веб-сайт портфолио может выглядеть так.

Вы можете использовать генератор статических сайтов, такой как Jekyll, или даже предопределенный шаблон, чтобы начать создавать свой сайт. Или, если вы хотите создать полностью настраиваемый веб-сайт, как я, вы можете сделать это с помощью HTML и CSS.

Точки данных в портфолио

Если вы посмотрите на структуру веб-сайта, вы увидите следующие данные:

  • Изображение профиля
  • Краткое содержание человека
  • Проекты и их детали
  • Лента блога
  • Лента социальных сетей

Изображение профиля

Рекомендуется загрузить изображение профиля в одном месте и везде использовать одно и то же. Таким образом, вы обновляете в одном месте, и оно отражается везде.

Есть несколько вариантов:

  • Gravatar: gravatar.com - это веб-сайт, на котором размещается ваше изображение профиля и предоставляется ссылка. Gravatar используется многими блогами для получения изображения вашего профиля на основе вашего электронного идентификатора.
  • Github: просто загрузите изображение своего профиля на github и используйте ту же ссылку в своем блоге.

Краткое содержание человека обычно не меняется время от времени. Следовательно, эта часть может оставаться статической и просто хранить данные в каком-то JSON или как статический контент на веб-сайте.

Проекты и их детали

Я предполагаю, что мы размещаем исходный код всех наших проектов на Github. Теперь Github предоставляет все проекты, которые вы размещали на нем, через API Github. Чтобы иметь доступ к API Github, необходимо сначала подписаться на программу для разработчиков Github. В общем, нам нужны следующие детали проекта через API:

  • Название проекта
  • Описание Проекта
  • Ссылка на проект
  • Звезды и вилки
  • теги, относящиеся к проекту

Хотя вы можете использовать REST API, предоставляемый Github, чтобы получить все эти данные для всех проектов, но для этого потребуется много обращений к серверу Github API. Вместо этого Github запустил свою версию API V4 GraphQL, с помощью которой вы можете просто сделать один вызов API и получить все сведения обо всех проектах.

Подробную ссылку на GraphQL Github API можно найти здесь https://developer.github.com/v4/guides/

Я использую следующий запрос:

   {
      user(login: "master-atul") {
        bio,
        avatarUrl,
        repositories (first :100 privacy: PUBLIC) {
          nodes {
          	name,
            url,
            stargazers{
              totalCount
            },
            owner {
              login
            }
            forks {
              totalCount
            },
            homepageUrl,
            description,
            repositoryTopics (first: 100){
              nodes {
                topic {
                  name
                }
              }
            }
          },
          pageInfo {
          	hasNextPage,
            endCursor
        	}
        }
      }
    }

Вы можете проверить это в проводнике GraphQL, предоставленном Github. Это помогает нам получить все необходимые данные с помощью одного обращения к API. 🤘🏻

Обход ограничений скорости в Github API

Но, в отличие от меня, у многих из вас может быть много пользователей, и множество пользователей означает множество обращений к API. Следовательно, вы можете достичь своего предела скорости API, и новые пользователи могут ничего не увидеть.

Чтобы обойти ограничения скорости API, мы будем использовать travis. Travis CI предоставляет бесплатную облачную CI для проектов с открытым исходным кодом.

Отсюда идея:

  • Travis CI посещает сервер API Github каждый день один или два раза и получает JSON с деталями нашего проекта.
  • Travis CI теперь помещает сгенерированный JSON в репозиторий нашего веб-сайта.
  • Затем Трэвис перестроит веб-сайт и развернет его заново.

Скрипты

Скрипт Github API

Целью скриптов Github было бы получить всю информацию о последних проектах из вашего репозитория github и поместить ее в файл json, который мы можем использовать на веб-сайте. Я собираюсь использовать node для написания скрипта github api, в первую очередь потому, что его легко выполнять извлечение и преобразование данных в node.

Мой сценарий выглядит примерно так:

projects_updater.js

var rp = require('request-promise');
var result = require('lodash/result');
var each = require('lodash/each');
var fs = require('fs');
var path = require('path');

var args = process.argv.slice(2);

const GITHUB_API_V4_READ_TOKEN = args[0];

const _githubFetcher = (query) => {
  const requestBody = {query};
  return rp({
    uri: 'https://api.github.com/graphql',
    method: 'post',
    json: true,
    headers: {
      'Authorization': `bearer ${GITHUB_API_V4_READ_TOKEN}`,
      'User-Agent': 'Request-Promise'
    },
    body: requestBody
  });
};

const _generateProjectReposQuery = (totalResults = 100, nextId = null, repositoryType) => {
  const offset = nextId ? `after:"${nextId}"` : '';
  return `{
      user(login: "master-atul") {
        bio,
        avatarUrl,
        ${repositoryType} (first :${totalResults} ${offset} privacy: PUBLIC) {
          nodes {
          	name,
            url,
            stargazers{
              totalCount
            },
            owner {
              login
            }
            forks {
              totalCount
            },
            homepageUrl,
            description,
            repositoryTopics (first: ${totalResults}){
              nodes {
                topic {
                  name
                }
              }
            }
          },
          pageInfo {
          	hasNextPage,
            endCursor
        	}
        }
      }
    }`;
};

const _recursiveProjectGetter = (queryParam, queryResult, repositoryType) => {
  const query = _generateProjectReposQuery(queryParam.totalResults, queryParam.nextId, repositoryType);
  return _githubFetcher(query).then((response) => {
    const nextPageInfo = result(response,  `data.user.${repositoryType}.pageInfo`, {});
    if (response.errors) {
      throw response.errors;
    }
    if (nextPageInfo.hasNextPage) {
      queryParam.nextId = nextPageInfo.endCursor;
      queryResult.push(response);
      return _recursiveProjectGetter(queryParam, queryResult, repositoryType);
    }
    queryResult.push(response);
    return queryResult;
  });
};

const _getMergedRepositoryInfo = (resultArray = [], repositoryType) => {
  const nodes = [];
  each(resultArray, (res) => {
    const resultNodes = result(res, `data.user.${repositoryType}.nodes`, []);
    nodes.push(...resultNodes);
  });
  return nodes;
};

const _writeJSON = (filename, data) => {
  const filePath = path.resolve(__dirname, '../app/assets/json', filename);
  fs.writeFileSync(filePath, data);
  return filePath;
};

const _getAllProjects = (repositoryType) => {
  const queryResult = [];
  const queryParam = {
    totalResults: 100,
    nextId: null
  };
  return _recursiveProjectGetter(queryParam, queryResult, repositoryType).
    then((data) => _getMergedRepositoryInfo(data, repositoryType));
};

// MAIN STATEMENT THAT EXECUTES THE ABOVE FUNCTIONS
// BASICALLY THIS GETS ALL THE REPOS AND CONTRIBUTIONS FROM GITHUB

Promise.all(
  [
    _getAllProjects('repositories').then((data) => _writeJSON('projects.json', JSON.stringify(data))),
    _getAllProjects('contributedRepositories').then((data) => _writeJSON('contributions.json', JSON.stringify(data)))
  ]
).then((filepaths) => console.log('updated files', filepaths)).catch((err) => {
  console.log(err);
  process.exit(-1);
});

Чтобы использовать этот сценарий, просто запустите node scripts/projects_updater.js <GITHUB_API_V4_READ_TOKEN>

Получение GITHUB_API_V4_READ_TOKEN

  1. Войдите на http://github.com
  2. Откройте https://github.com/settings/tokens
  3. Щелкните Создать новый токен.
  4. Дайте эти разрешения: public_repo, read: user, user: email.
  5. Убедитесь, что вы скопировали токен и сохранили его.

Travis CI - Скрипт развертывания

Я использовал webpack для создания последнего статического актива для своего сайта. Я использую Travis для автоматизации сборки всякий раз, когда мы нажимаем на репо. Для этого я написал сценарий сборки, который должен выполняться при каждом нажатии на репо.

build.sh

#!/bin/bash
set -e # Exit with nonzero exit code if anything fails

SOURCE_BRANCH="source"
TARGET_BRANCH="master"

function updateProjects {
  yarn install &&
  node scripts/projects_updater.js ${GITHUB_API_V4_READ_TOKEN} &&
  yarn build
}

function create_all_branches {
    # Keep track of where Travis put us.
    # We are on a detached head, and we need to be able to go back to it.
    local build_head=$(git rev-parse HEAD)

    # Fetch all the remote branches. Travis clones with `--depth`, which
    # implies `--single-branch`, so we need to overwrite remote.origin.fetch to
    # do that.
    git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
    git fetch
    # optionally, we can also fetch the tags
    git fetch --tags

    # create the tacking branches
    for branch in $(git branch -r|grep -v HEAD) ; do
        git checkout -qf ${branch#origin/}
    done

    # finally, go back to where we were at the beginning
    git checkout ${build_head}
}


# Variables
REPO="https://github.com/master-atul/master-atul.github.io.git"
SSH_REPO=${REPO/https:\/\/github.com\//[email protected]:}

git config user.name "Atul R"
git config user.email "[email protected]"

#this is necessary because by default travis only shallow clones the master branch
create_all_branches &&

git checkout $SOURCE_BRANCH

#updating the project by running the build
updateProjects

#copying important assets to a temporary directory
cp -rf bundle ../portfolio_dist
cp CNAME /tmp/CNAME
cp README /tmp/README

#cleanup before changing the branch
git stash
git clean -fd

git checkout $TARGET_BRANCH
cd ..
echo "removing old dist"
rm -rf ./master-atul.github.io/**
cd master-atul.github.io
echo "copying new dist"
cp -rf ../portfolio_dist/* .
echo "current directory ${pwd}"
ls ../portfolio_dist
cp /tmp/CNAME CNAME
cp /tmp/README README

git add -A .
set +e
git commit -m "Commit new bundle to ${TARGET_BRANCH}"
set -e
git push $SSH_REPO $TARGET_BRANCH

Чтобы запустить сценарий: bash ./scripts/build.sh

В github нам нужно сохранить последний dist в ветке master репозитория портфеля. Таким образом, TARGET_BRANCH является главной ветвью, и мы храним наш некомпилированный исходный код в SOURCE_BRANCH. Мой SOURCE_BRANCH - source.

Следовательно, мы сохраняем наш исходный код в SOURCE_BRANCH и продолжаем продвигать любые изменения, которые мы там вносим. Travis CI возьмет исходный код из SOURCE_BRANCH, а затем построит статические активы и отправит окончательные созданные активы в TARGET_BRANCH. Github заберет код из основной ветки и будет обслуживать наш сайт.

Теперь мне просто нужно расслабиться и сосредоточиться на своей работе, а мое портфолио само обновится 🍷 😎

Надеюсь, это поможет людям создавать потрясающие автоматизированные портфолио! Ура 🍻 🌮

Вы можете увидеть живой пример на http://atulr.com

Первоначально опубликовано на сайте atulr.com 9 октября 2017 г.