Skip to content

Instantly share code, notes, and snippets.

@withchandra
Last active April 16, 2022 00:02
Show Gist options
  • Save withchandra/4459e9095dac0e21aae996dde2fb788b to your computer and use it in GitHub Desktop.
Save withchandra/4459e9095dac0e21aae996dde2fb788b to your computer and use it in GitHub Desktop.
Laravel + PHP CS Fixer + Prettier + ESLint + airbnb-base + Husky + lint-staged + commitlint + pre-commit hook + pre-push hook

This is my personal setup for linting and repo branches protection for a new laravel project. Feel free to copy and adjust.

Reason

I am using both bitbucket and github for mostly private projects, while bitbucket allows private repo to use advance features such as branch permission, github limit their protected branch feature for free-user.

On the other hand, implementing standard linting for both JS and PHP will help code-reviewer to read and evaluate the code.

Last but not least, standardized commit message will make everybody happy. =)

Step by Step Installation

1. Prepare your laravel project repo

Prepare your laravel project first and do initial deploy to your develop branch first without any linting and whatsoever. (or master branch if you don't follow git-flow rules) Read: https://nvie.com/posts/a-successful-git-branching-model/

Go to your local project repository and do the following steps.

2. Install all necessary dependencies

npm install --save-dev husky lint-staged prettier
npm install --save-dev eslint eslint-plugin-vue@next
npx install-peerdeps --dev eslint-config-airbnb-base
npm install --save-dev @commitlint/{cli,config-conventional}

echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js

composer require friendsofphp/php-cs-fixer

3. Prepare .php_cs config file

This file contains all setting for your php-cs-fixer module. Copy, adjust and put it in your root repository folder.
Read: https://github.com/FriendsOfPHP/PHP-CS-Fixer#Rules

sample file attached below

4. Prepare .eslintrc.js file

This file contains all eslint configuration setting. Copy, adjust and put it in your root repository folder.
Read: https://eslint.org/docs/user-guide/configuring

sample file attached below

5. Download pre-push and pre-commit bash scripts

These bash scripts will do several things:

  • This script will block anybody who tries to push to a certain branch (in my case I don't want anybody -including myself- to push directly to master and develop branch). They need to work in their own branch and then create a pull request.
  • This script will block anybody who tries to push to a branch that is different from their current active branch. For example you are in branch fix/someissue but then you mistakenly type git push origin master.
  • This script will block anybody who tries to commit to a certain branch (in my case master and develop branch).
  • This script will block anybody who tries to commit to a branch that has ascii character in it.
  • This script will block anybody who tries to commit to a branch that has upper case character in it.

Follow the instruction from here: https://github.com/talenavi/husky-precommit-prepush-githooks

Don't forget to give executable permission for the bash script

sudo chmod +x commands/pre-*

6. Modify package.json file

Since we are using husky (https://github.com/typicode/husky) and lint-staged (https://github.com/okonet/lint-staged) everything will be much easier.
Husky will handle any pre-push hook, pre-commit hook and commit-msg hook.
Lint-Staged will select only certain files in that particular commit and those files will be checked by eslint and prettier etc.

Add husky configuration in package.json or separated config file (I prefer to use package.json tho.)

    ...
    "devDependencies": {
        ....
    },
    "husky": {
        "hooks": {
            "pre-commit": "./commands/pre-commit && lint-staged",
            "pre-push": "./commands/pre-push $HUSKY_GIT_STDIN",
            "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
        }
    },
    "lint-staged": {
        ...
    },
    ...

explanation

During pre-commit hook: we will run pre-commit bash script and everything in lint-staged configuration.
During pre-push hook: we will run pre-push bash script and send husky stdin.
During git commit phase: we will run commitlint for evaluate the text of each commit using commitlint.

You may configure your lint-staged and prettire configuration in here too

Please see the full sample and figure out how to do it by yourself or read this: https://github.com/okonet/lint-staged#configuration

full sample file attached below

7. Modify .editorconfig (optional)

Somehow my sublime and vscode configuration automatically replace single-quote with double-quote for any string in JS file.
Hence, I need to overwrite it in .editorconfig (It will replace all other programmer's editor config in their local too)

Add this lines inside the file

...
[*.js]
quote_type = single
...

sample file attached below

8. Enable Github Actions CI/CD for php cs fixer (optional)

I know it is a little bit redundant, since I already implement php cs fixer locally through husky and lint-staged.
However as you know, all of those local rules can be easily bypassed by --no-verify.
I want to make sure at least the php-cs-fixer run on every pull request using Github Actions (free-plan private repo got only 2000 build-minutes/month)

create a folder .github/workflows/ in your root repository and put this lint.yml there.

Copy and paste this content to lint.yml

on: [pull_request]
name: Main
jobs:
  php-cs-fixer:
    name: PHP-CS-Fixer
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: PHP-CS-Fixer
      uses: docker://oskarstark/php-cs-fixer-ga
      with:
        args: --config=.php_cs

Read: https://github.com/marketplace/actions/oskar-php-cs-fixer

Credits

All of these scripts I found somewhere in the internet and cannot mentioned one by one.
Of course there are many parts that I modified to suit my need.
So please feel free to copy and adjust to your needs.

If you think this article helps you,
You can buy me a coffee ☕ https://www.buymeacoffee.com/christchan

Thank you and have a nice day!
www.talenavi.com
www.christianchandra.com

root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[*.js]
quote_type = single
module.exports = {
env: {
browser: true,
es6: true,
commonjs: true,
},
extends: [
'airbnb-base',
'eslint:recommended',
'plugin:vue/strongly-recommended',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
Vue: true,
axios: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
'no-console': 2,
'no-empty': 0,
'consistent-return': 0,
'no-await-in-loop': 0,
indent: ['error', 4],
'@vue/comment-directive': 0,
},
};
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude(['bootstrap', 'storage', 'vendor'])
->name('*.php')
->name('_ide_helper')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sortAlgorithm' => 'alpha'],
'no_unused_imports' => true,
])
->setFinder($finder);
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.1",
"@prettier/plugin-php": "^0.14.3",
"axios": "^0.19",
"cross-env": "^7.0",
"eslint": "^7.6.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-vue": "^7.0.0-beta.2",
"husky": "^4.2.5",
"laravel-mix": "^5.0.1",
"lint-staged": "^10.2.11",
"lodash": "^4.17.13",
"prettier": "^2.0.5",
"resolve-url-loader": "^3.1.0",
"sass": "^1.15.2",
"sass-loader": "^8.0.0"
},
"husky": {
"hooks": {
"pre-commit": "./commands/pre-commit && lint-staged",
"pre-push": "./commands/pre-push $HUSKY_GIT_STDIN",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"resources/**/*.{js,jsx,vue}": [
"eslint --fix-dry-run"
],
"resources/**/*.{css,js}": [
"prettier --write"
],
"**/*.php": [
"prettier --write",
"php ./vendor/bin/php-cs-fixer fix --config .php_cs --allow-risky=yes"
]
},
"prettier":{
"printWidth": 120
}
}
@mreduar
Copy link

mreduar commented Mar 30, 2022

Do you use prettier and eslint only when committing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment