Wilson Mar bio photo

Wilson Mar

Hello!

Calendar YouTube Github

LinkedIn

How we configure each GitHub repo to maximize teamwork

US (English)   Norsk (Norwegian)   Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Estonian   اَلْعَرَبِيَّةُ (Egypt Arabic)   Napali   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean

Overview

This article describes how to configure GitHub to maximize teamwork.

NOTE: Content here are my personal opinions, and not intended to represent any employer (past or present). “PROTIP:” here highlight information I haven’t seen elsewhere on the internet because it is hard-won, little-know but significant facts based on my personal research and experience.

.gitignore in root folder

This defines all the folders and files which should NOT be pushed up to GitHub for the team to see. We look at this first because it summarizes many of the various utilities used:

See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# macOS:
.DS_Store
 
# AWS CDK
infrastructure/cdk.out
 
# NodeJs dependencies:
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
 
/.pnp
.pnp.js
 
# testing
/coverage
 
# production
/build
 
# misc
.env.local
.env.development.local
.env.test.local
.env.production.local
 
# ReactJs:
webapp/public/config.json
webapp/build
   

package.json in root

For NodeJs developers, there are two competing utilities to automate install of dependencies specified in import statements within NodeJs code:: npm vs. yarn:

  • yarn users create a yarn.json file.

  • npm users create a package.json file

Here is a sample package.json file:

{
  "name": "ps-serverless-app",
  "private": true,
  "workspaces": [
    "infrastructure",
    "webapp",
    "services/*"
  ],
  "scripts": {
    "lint": "npx eslint . --fix --ext .js,.ts,.jsx",  
    "test": "",
    "load:sampleData": "aws dynamodb batch-write-item --request-items file://sampleData.json"
  },
  "devDependencies": {
    "@types/jest": "^26.0.15",
    "@typescript-eslint/eslint-plugin": "^4.6.1",
    "@typescript-eslint/parser": "^4.6.1",
    "@babel/eslint-parser": "^7.13.14",
    "eslint-config-airbnb": "^18.2.1",
    "esbuild": "0",
    "eslint": "^7.12.1",
    "eslint-config-airbnb-base": "^14.2.1",
    "eslint-config-airbnb-typescript": "^12.0.0",
    "eslint-config-airbnb-typescript-prettier": "^3.1.0",
    "eslint-config-prettier": "^6.15.0",
    "eslint-import-resolver-node": "^0.3.4",
    "eslint-import-resolver-typescript": "^2.3.0",
    "eslint-plugin-eslint-comments": "^3.2.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jest": "^24.1.0",
    "eslint-plugin-prettier": "^3.1.4",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-simple-import-sort": "^5.0.3",
    "eslint-plugin-unicorn": "^23.0.0",
    "husky": "^4.3.0",
    "jest-html-reporter": "^3.3.0",
    "jest-junit": "^12.0.0",
    "lint-staged": "^10.5.1",
    "prettier": "^2.1.2",
    "prettier-eslint": "^11.0.0",
    "ts-jest": "^26.4.3",
    "typescript": "^4.0.5"
  },
  "resolutions": {
    "@typescript-eslint/eslint-plugin": "^4.6.1",
    "@typescript-eslint/parser": "^4.6.1",
    "jest": "26.6.0"
  }
}
   
  • “dependencies” are packages required by the app in production.

  • “devDependencies” are packages that are only needed for local development and testing.

  • “resolutions” specifies custom package versions or ranges instead of waiting for the author of transitive sub-dependencies to update their package dependencies (to resolve a GitHub Dependabot alert). Or your dependency defines a broad version range and your sub-dependency has a problematic update so you want to pin it to an earlier version.

      "resolutions": {
      "@typescript-eslint/eslint-plugin": "^4.6.1",
      "@typescript-eslint/parser": "^4.6.1",
      "jest": "26.6.0"
     

So “jest”: “26.6.0” serves to specify a version without the security vulnerability which should be used inside transitive dependencies.

If you use yarn, the above is instead of manual edits in the yarn.lock file before running yarn install.

Scripts in npm package.json

References:

  • https://levelup.gitconnected.com/understanding-dependency-management-with-node-modules-1c47bcdee98b
  • https://medium.com/learnwithrahul/understanding-npm-dependency-resolution-84a24180901b
  • https://www.digitalocean.com/community/tutorials/how-to-use-node-js-modules-with-npm-and-package-json
  • https://juffalow.com/blog/javascript/how-yarn-resolutions-can-save-you/
  • https://www.linkedin.com/learning/github-dependabot-dependency-updates/get-started-with-dependabot

However, if you use npm rather than yarn, you can change the package-lock.json file and running npm, use overrides use github.com/rogeriochaves/npm-force-resolutions which forces the installation of specific version of a transitive dependency, as a last resort after contacting maintainers of top-level dependency libraries.

  1. The utility uses Clojure, which requires Java JDK 6+. Clojure has the Leiningen build tool. So install Clojure (one time on the macOS machine running npm)

    https://gist.github.com/fscm/4e9b719c7556997d54068556a7101630

    brew install clojure/tools/clojure

    NOTE: Clojure has rlwarp for tab completions and parenthesis matching.

  2. Verify install:

    which clojure

    Response:

    /usr/local/bin/clojure
  3. Cleanup: remove the downloaded installers:

    rm -rf "${HOME}/.src"
  4. Clojure has an uncommon way to check whether it is installed:

    clj -e '(+ 1 1)'
    

    should return 2 and take you back to the Terminal prompt.

    NOTE: clj -e nil returns “WARNING: Implicit use of clojure.main with options is deprecated, use -M” and puts you in the Clojure REPL “user=>”.

  5. To invoke the script using npx, add to package-json

      {,
      "scripts": {
     "preinstall": "npx npm-force-resolutions"
     
  6. Run npm install as you would normally do at your project’s folder:

    npm install
  7. Use a text editor to open the package-lock.json file created:

    cat package-lock.json

    This file should be comitted and pushed to GitHub (it should not be specified within .gitignore).

  8. Verify that the package version has been updated to the new value.

ESLint install

  1. In package.json, npx eslint temporarily installs and runs eslint (on every run).
      "resolutions": {
     "@typescript-eslint/eslint-plugin": "^4.6.1",
     "@typescript-eslint/parser": "^4.6.1",
     
  2. In .vscode/settings.json

    This invokes ESLint on each save:

    {
      "editor.codeActionsOnSave": {
     "source.fixAll.eslint": true
      }
    }
    

    ESLint catches “code smells” according to a set of rules hopefully agreed upon by the team, which is why it’s in the team’s GitHub instead of locally on your laptop.

  3. In file .eslintignore

    dist
    cdk.out
    node_modules
    build
    
  4. In file .eslintrc

    {
      "root": true,
      "parser": "@babel/eslint-parser",
      "parserOptions": {
     "requireConfigFile": false
      }
    }
    

babel.config.js in root

  1. Within babel.config.js in root folder:

    module.exports = {
      presets: ['@babel/preset-env', '@babel/preset-react'],
      targets: {
     node: 'current',
      },
    };