Skip to content

[rush] Integrate git hooks to rush change when git push #1042

Open

Description

@lijunle

The rush change is cool to ensure the same version in the repo. The CI is set up to be fast failed if rush rush is not passing verified. However, it could be better to integrate git hooks to fail git push if the rush change --verify is not passing.

Activity

  1. changed the title [-]Integrate git hooks to rush change when git push[/-] [+][rush] Integrate git hooks to rush change when git push[/+] on Jan 20, 2019
  2. octogonz commented on Jan 20, 2019

    @octogonz
    Collaborator

    I think this is solvable using the current feature set. I'll see if I can write some documentation on the web site explaining how to set this up.

  3. Niek commented on Feb 17, 2021

    @Niek
    Contributor

    Does anybody have an example githook that runs rush change when pushing and no changes are created yet?

  4. elliot-nelson commented on Nov 29, 2022

    @elliot-nelson
    Collaborator

    I'll share our current pre-push hook (this file lives at common/git-hooks/pre-push).

    #!/bin/sh
    # Called by "git push" with no arguments.  The hook should
    # exit with non-zero status after issuing an appropriate message if
    # it wants to stop the push.
    
    # These switches turn some silent bugs into errors and immediately terminate the script on error
    # set -o errexit -o noclobber -o nounset
    
    # git lfs
    command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; }
    git lfs pre-push "$@"
    
    # Note that "git lfs" filter is handled first, before extra validation.
    #
    # Validation on "pre-push" is tricky, as the developer may have many legitimate reasons to bypass
    # and push up their changes (peer review, saving work in progress etc.).
    #
    # If the next step for developer is "git push --no-verify", we will skip this hook; but if the lfs pre-push
    # hasn't run then GitHub will also reject the push on the server side due to invalid lfs references. To
    # prevent this, we make sure lfs pre-push runs even if the below validation might fail.
    
    # Custom hooks below
    
    # If using nvm on OS X then we have to source nvm.sh and call nvm use to make sure we select the correct version of node.
    # Normally a login shell will do this for us but GUI apps do not run the init scripts that login shells do.
    
    # See if nvm is installed (from official documentation https://github.com/nvm-sh/nvm#verify-installation)
    if [ "$(command -v nvm)" = "nvm " ]; then
        # NVM is available in this environment, just use it
        nvm use
    else
        # NVM is not available, we may not be in a login shell
        # Search well known locations for nvm.sh which we will use to initialize nvm before calling "nvm use"
        export NVM_DIR="${HOME}/.nvm"
    
        # See if nvm was installed via the official instructions (most common case)
        if [ -s "${NVM_DIR}/nvm.sh" ]; then
            . "${NVM_DIR}/nvm.sh" --no-use
            nvm use
    
        # See if nvm was installed via brew (less common but possible)
        elif [ -s "$(brew --prefix nvm)/nvm.sh" ]; then
            . "$(brew --prefix nvm)/nvm.sh" --no-use
            nvm use
    
        # NVM not found, calls to node will probably fail. Warn the caller and hope for the best
        else
            echo 'Unable to detect nvm, node scripts may fail. (tried searching with "brew --prefix nvm" and "ls ${HOME}/.nvm")'
        fi
    fi
    
    # Checks below will all occur in a sequence even if one of them fails. If the developer has
    # multiple issues to fix before pushing, we want to give them the maximum amount of feedback
    # we can in a single cycle.
    
    EXIT_CODE=0
    node common/scripts/install-run-rush.js --quiet install --check-only --bypass-policy || EXIT_CODE=1
    node common/scripts/install-run-rush.js --quiet change --no-fetch --verify || EXIT_CODE=1
    
    exit ${EXIT_CODE}

    It's a bit long, but hopefully is a useful reference -- these hooks evolve over time to support your developers on different operating systems, using different CLI and GUI tools to do git pushes, etc.

    The LFS thing deserves a special note -- it's (typically) pretty important to have LFS set up early in a monorepo to handle binary assets and such, but you can't let git lfs install own your pre-push hook if you want extra validation in it. In the example above we've copied in git-lfs's manually into the appropriate place in the hook, and this is accompanied by logic in our global setup script, common/scripts/setup.sh:

    _installGitLFS() {
      echo Checking Git LFS...
      command -v git-lfs >/dev/null
      if [ $? -ne 0 ]; then
        echo Installing Git LFS...
        if [[ "$OSTYPE" == "darwin"* ]]; then
          set -e
          brew install git-lfs
          set +e
        else
          printf $RED
          echo "Windows/Linux users, please manually install git-lfs:" >&2
          echo "  https://docs.github.com/en/github/managing-large-files/installing-git-large-file-storage" >&2
          echo >&2
          printf $RESET
          exit 1
        fi
      fi
    }
    
    _setupRepo() {
      # Rush fully controls git hooks within the monorepo, and the git lfs calls are
      # alreay baked in. So, we rely on git lfs install to install the smudge filters
      # if they don't exist, and if it fails to update hooks we don't care.
      echo "Configuring LFS hook..."
      git lfs install 2>/dev/null
    
      set -e
    
      echo "Ensuring LFS is up-to-date..."
      git lfs pull
    
      # Install the "ours" merge driver, which is a useful way to deal with certain
      # hard-to-merge text files.
      echo "Configuring merge drivers..."
      git config merge.ours.driver true
    
      set +e
    }

    The command git lfs install does two things: first it installs the smudge filters for your local repo, then it tries to update your pre-push hook, which will fail if Rush has already written a different one. So we intentionally throw away the error from git lfs install, and then run a git lfs pull manually, just in case there is a local file that should have been smudged already and wasn't (basically this is a first-time user of the monorepo that didn't follow instructions and run our setup script).

    Pete Gonzalez (@octogonz) Some of the above might be useful either as additional documentation, or even a large "example" hook script with various parts commented out, that you could uncomment to activate.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    general discussionNot a bug or enhancement, just a discussion

    Type

    No type

    Projects

    • Status
      Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions