DevGang
Авторизоваться

Как использовать Git Hooks в проектах JavaScript

Git hooks автоматически запускают пользовательские сценарии, когда в репозитории Git происходит определенное событие. Есть две группы hooks: клиентские и серверные.

Вы можете ознакомиться с полной документацией для Git hooks, но в данной статье расскажу только о pre-commit и pre-push. Вы сможете написать свой собственный скрип с нуля, либо проверить готовые инструменты в репозитории GitHub.

Проблема(ы)

Можете ли вы представить, что над проектом работает много разработчиков, и у каждого свой стиль кода? Да, я знаю, что есть инструменты под названием ESLint, Prettier и Stylelint, но как убедиться, что все установили его в свою IDE или запускают команду в ручную?

Возможно, в ваш проект был интегрирован Sentry, и вам необходимо обновить выпуск с основной версии на младшую. Люди совершают ошибки: мы не можем гарантировать, что никогда не забудем обновить релиз Sentry перед отправкой коммитов.

Решения

Поскольку нам нужны hooks как pre-commit, так и pre-push, наш выбор оставили Husky and Lefthook. Husky получил более простую конфигурацию по сравнению Lefthook. Затем нам понадобится помощь от lint-staged, чтобы заставить всех следовать стандарту, прежде чем отправлять свой код в репозиторий; найдите строку выпуска Sentry и замените ее правильной версией в pre-push.

Приступаем к работе

Установка:

npm i -D husky lint-staged
// or
pnpm i -D husky lint-staged
// or
yarn add husky lint-staged -D

Включаем Git hooks:

npx husky install

Убеждаемся, что у каждого разработчика в проекте включены свои Git hooks. Сделайте это в своем package.json, который автоматически включит husky всякий раз, когда вы запускаете npm install локально.

// package.json
{   
  "scripts": {     
    "prepare": "husky install"   
  } 
}

Pre-commit

Добавьте нижеприведенный фрагмент в package.json:

// package.json
{
  "lint-staged": {
    "*.{js,vue}": "eslint --cache --fix"
  }
}

Приведенный выше фрагмент можно понять следующим образом: всякий раз, когда файл с расширением js или vue был подготовлен, он запускает команду eslint --cache --fix. Это позволит попытаться исправить проблему ESLint, когда это возможно. В противном случае он вернет ошибку(и), и вам придется исправить ее, прежде чем совершать ее снова.

Создайте файл по адресу .husky/pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

Когда вы запускаете git commit, команда npx lint-staged будет выполнятся перед успешной фиксацией.

Pre-push

Нам нужен собственный скрипт для обновления версии Sentry перед отправкой.

update-sentry-release.js
const fs = require("fs");
const path = require("path");
const readline = require("readline");
const git = require("isomorphic-git");
const dayjs = require("dayjs");

const DIR = path.resolve(__dirname, "../");
const TARGET_FILE = "vite.config.js"; // TODO: REPLACE

const getReleaseName = async () => {
  const branch = await git.currentBranch({
    fs,
    dir: DIR,
  });

  let release = "";
  // Only update the release when it's a release or hotfix branch
  if (branch.startsWith("release/")) {
    release = `release@${dayjs().format("YYYY.MM.DD")}`; // TODO: REPLACE
  } else if (branch.startsWith("hotfix/")) {
    release = `${branch.replace("/", "@")}@${dayjs().format("YYYY.MM.DD")}`; // TODO: REPLACE
  }
  return release;
};

(async () => {
  const target = path.resolve(DIR, TARGET_FILE);
  const fileStream = fs.createReadStream(target);
  let tempStr = "";

  const release = await getReleaseName();
  if (release === "") return;

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity,
  });

  for await (const line of rl) {
    if (line.includes('release: "')) {
      const matches = line.match(/"(.*?)"/);
      const currentRelease = matches ? matches[1] : line;
      if (currentRelease === release) return;
      tempStr += `\n${line.replace(/"([^"]+)"/g, `"${release}"`)}`;
    } else {
      tempStr += `\n${line}`;
    }
  }
  fs.writeFileSync(target, tempStr.trimStart() + "\n", {
    flag: "w",
  });
  const status = await git.status({ fs, dir: DIR, filepath: TARGET_FILE });
  if (status === "*modified") {
    // stage and commit the updated file
    await git.add({ fs, dir: DIR, filepath: TARGET_FILE });
    await git.commit({
      fs,
      dir: DIR,
      author: {
        name: "Oyster Lee", // TODO: REPLACE
        email: "oysterd3@gmail.com", // TODO: REPLACE
        timezoneOffset: 8, // TODO: REPLACE
      },
      message: `🔖 chore(automated): update sentry release`,
    });
  }
})();

Релиз Sentry находится в vite.config.js, таким образом, скрипт найдет строку, в которой ключевое слово release: и заменит ее правильным именем релиза.

Добавьте приведенный ниже фрагмент в package.json

// package.json
{
  "scripts": {
    "prepush": "node scripts/update-sentry-release.js"
  }
}

И создайте файл .husky/pre-push с помощью приведенного ниже фрагмента

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run prepush

Известные проблемы

lint-staged выдаст ошибку при попытке зафиксировать много промежуточных файлов в Windows. Проверьте открытую проблему.

Пользовательский скрипт update-sentry-release работает только при отсутствии разрыва строки.

Когда кто-то обходит Git hooks и вы объединяете их коммиты, вам придется исправлять их ошибки linter.

#JavaScript #Git
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу