Skip to content

Instantly share code, notes, and snippets.

@Bunyod
Last active November 12, 2019 08:50
Show Gist options
  • Save Bunyod/9f4ba570b9ce7c13d94025c070a499b8 to your computer and use it in GitHub Desktop.
Save Bunyod/9f4ba570b9ce7c13d94025c070a499b8 to your computer and use it in GitHub Desktop.

Git hook manager

pre-commit-hook vs overcommit

Both are tools tool to manage and configure Git hooks. These tools don’t allow you to push if in your code something is not satisfactory with your configuration file. Configuration files:

  • .scalafmt - use by pre-commit-hook (by default)
  • scalastyle-config.xml - use by overcommit (by default)

What is overcommit for Scala developer?

overcommit is a Git hook manager that includes out-of-the-box support for running scalastyle as a Git pre-commit hook. It doens't support scalafmt git hook by default.

What is pre-commit for Scala developer?

pre-commit-hook is a Git hook manager that runs scalafmt on CHANGED .scala and .sbt files each time you commit them. It support only local git hook for scalastyle and for that your local machine should has a runnable scalastyle from commandline.

Which one we should consider to use?

Please add your opinion? I would go with second approach. See Alternative B

There are several alternatives to run both in a committing stage:

  • use pre-commit-hook with scalafmt and setup scalastyle for your project as a part of compilation. See Alternative A
  • use pre-commit-hook with scalafmt (allows to store hooks on Github) and scalastyle (local git hook). See Alternative B
  • use overcommit with scalastyle and and add custom script. See Alternative C
  • write custom script and add it to your .git/hooks folder or run it manually like: bash ./pre-commit-hook.sh

Alternative A

The latest version of sbt has support Def.sequential function to run tasks under semi-sequential semantics.

How to setup scalastyle as a part of compile time: add scalastyle plugin to your project/style.sbt or project/plugins.sbt

addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")

in build.sbt

lazy val compilecheck = taskKey[Unit]("compile and then scalastyle")

lazy val root = (project in file("."))
  .settings(
    compilecheck in Compile := Def.sequential(
      compile in Compile,
      (scalastyle in Compile).toTask("")
    ).value
  )

To call this task type in compilecheck from the shell. If the compilation fails, compilecheck would stop the execution.

root> compilecheck
[info] Compiling 1 Scala source to /Users/x/proj/target/scala-2.10/classes...
[error] /Users/x/proj/src/main/scala/Foo.scala:3: Unmatched closing brace '}' ignored here
[error] }
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed

Alternative B

We could just add a hook to our pre-commit-config.yaml which looks like this:

repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v2.0.0
    hooks:
    -   id: check-json
    -   id: check-merge-conflict
    -   id: check-xml
    -   id: check-yaml
    -   id: detect-private-key
-   repo: [email protected]:coyainsurance/pre-commit-scalafmt.git
    sha: master
    hooks:
    - id: scalafmt
      args: [-t ]

-   repo: local
    hooks:
    -   id: scalastyle
        name: scalastyle
        entry: scalastyle --config scalastyle-config.xml
        language: system
        files: \.(scala|sbt)$
        types: [file, text]

Alternative C

Create a custom-script for running scalafmt:

#!/usr/bin/env bash
# This script creates a pre-commit git hook that run scalafmt before each commit
# http://scalameta.org/scalafmt/
# Need scalafmt installed in CLI mode.
[ -d .git/hooks/ ] || (echo "It's not a git directory";exit 1)
[ -d .git/hooks/ ] && echo '#!/usr/bin/env bash
echo -e "\e[0;33m Scalafmt RUNNING \e[0m"
scalafmt --git true --diff-branch $(git branch | head -n1 | cut -d " "  -f2)
RESULT=$?
if [ ${RESULT} -ne 0 ]; then
    echo -e "\e[0;31m Scalafmt FAILED \e[0m"
    exit ${RESULT}
fi
echo -e "\e[0;32m Scalafmt SUCCEEDED \e[0m"
exit 0

And add your .sh file as a part of pre-commit stage:

PreCommit:
  RuboCop:
    enabled: true
    on_warn: pass # Treat all warnings as failures
  Scalastyle:
    enabled: true
    flags: ['-c', 'scalastyle-config.xml']
  CustomScript:
    enabled: true
    required_executable: './scalafmt.sh'

Note:

Here I want to mention also about Jenkins: Remove scalastyle and scalafmt runners. If we setup these two things on each developer machine we don't need to worry about style and format on prod or stage machine. I think this will also help to reduce duration of running the Jenkins task.

References:

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