21

Gitlab has functionality to generade badges about build status and coverage percentage.
Is it possible to create custom badge to display Pylint results? Or just display this results in README.md?
I already have CI job for Pylint

Djent
  • 2,877
  • 10
  • 41
  • 66
  • What format does Pylint display results in? Success/Failure or percents? – Jakub Kania Mar 31 '17 at 19:08
  • The result of PyLint is rating. For example: 7.5/10 – Djent Apr 06 '17 at 08:53
  • Did you ever get a version of this working? Also, how are you integrating gitlab-ci with your pylint? I just tried it and hit a snag because pylint returns aren't simple 0==ok. – Dan R Jun 27 '17 at 21:20
  • [Dan R](https://stackoverflow.com/users/955121/dan-r) I wrote a script for handling and translating pylint exit codes - https://github.com/jongracecox/pylint-exit – JGC Dec 07 '17 at 21:36

5 Answers5

43

I have written a python badge generation package that produces badges very visually similar to the main badge services. It is highly flexible, you can import and use in your python code, or run from the command line.

I use this in GitLab CI to display pylint and coverage scores.

There are other ways to do this using shields.io (see other answer from kubouch), but this approach can be used in situations where you may not have external internet access, such as in a corporate / enterprise setting where firewalls or proxies are blocking internet access.

GitLab CI Setup

1. Generate the badge

My CI pipeline has a step that runs pylint, and I used sed to extract the score from the output text. I then use anybadge (details below) to generate a pylint score badge, and save it as public/pylint.svg.

pylint:
  stage: test
  script:
    - pylint --rcfile=.pylintrc --output-format=text <LIST-OF-FILES-TO-RUN-PYLINT-AGAINST> | tee pylint.txt
    - score=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' pylint.txt)
    - echo "Pylint score was $score"
    - anybadge --value=$score --file=public/pylint.svg pylint

If pylint generates a non-zero rc then GitLab will see that as a command error and the job will fail, meaning no badge is generated, and missing image will show where the badge is used.

NOTE: pylint WILL OFTEN generate non-zero return codes since it uses the exit code to communicate the status of the lint check. I suggest using something like pylint-exit to handle pylint return codes in CI pipelines.

2. Register badge as pipeline artifact

I register the generated badge file as an artifact in the CI job by including this in the .gitlab-ci.yml:

pylint:
    ...
    - echo "Pylint score was $score"
    - anybadge --value=$score --file=public/pylint.svg pylint
  artifacts:
    paths:
      - public/pylint.svg

3. Publish badge to GitLab Pages

I include a pages publish step, which deploys everything in the public directory to GitLab pages:

pages:
  stage: deploy
  artifacts:
    paths:
    - public
  only:
  - master

4. Include badge in README.md

When the master pipeline runs for the project, the pylint.svg file is published to GitLab Pages, and I can then reference the image from my project README.md so that the latest pylint badge is displayed.

If you are using https://gitlab.com for your project then the URL for the svg artifact will usually be something like this (replace NAMESPACE with your username, or group name if your project is under a group - more details here):

https://NAMESPACE.gitlab.io/pyling.svg

In your README.md you can include an image with:

![pylint](https://NAMESPACE.gitlab.io/pyling.svg)

If you want to make the image into a link you can use:

[![pylint](https://NAMESPACE.gitlab.io/pyling.svg)](LINKTARGET)

Let me know if you need more information on any of the setup.

Anybadge Python Package

Here's some more info on the anybadge Python package:

You can set the badge label and value, and you can set the color based on thresholds. There are pre-built settings for pylint, coverage, and pipeline success, but you can create any badge you like.

Here is a link to the github project with more detailed documentation: https://github.com/jongracecox/anybadge

Install with pip install anybadge

Example python code:

import anybadge

# Define thresholds: <2=red, <4=orange <8=yellow <10=green
thresholds = {2: 'red',
              4: 'orange',
              6: 'yellow',
              10: 'green'}

badge = anybadge.Badge('pylint', 2.22, thresholds=thresholds)

badge.write_badge('pylint.svg')

Example command line use:

anybadge --label pylint --value 2.22 --file pylint.svg 2=red 4=orange 8=yellow 10=green

Update 2019

Using GitLab Pages is no longer required

It is now possible to directly access to the latest articfact, which simplify the workaround.

  1. Use a dedicated pylint artifact instead of public, and remove the unnecessary deploy step (or edit it if already used):
pylint:
  stage: test
  before_script:
    - pip install pylint pylint-exit anybadge
  script:
    - mkdir ./pylint
    - pylint --output-format=text . | tee ./pylint/pylint.log || pylint-exit $?
    - PYLINT_SCORE=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' ./pylint/pylint.log)
    - anybadge --label=Pylint --file=pylint/pylint.svg --value=$PYLINT_SCORE 2=red 4=orange 8=yellow 10=green
    - echo "Pylint score is $PYLINT_SCORE"
  artifacts:
    paths:
      - ./pylint/

Note that here I copy the Pylint log file in the folder artifact, in this way it will be accessible without looking at the pipeline logs.

The badge image will then be available at https://gitlab.example.com/john-doe/foo/-/jobs/artifacts/master/raw/pylint/pylint.svg?job=pylint, and the Pylint log at https://gitlab.example.com/john-doe/foo/-/jobs/artifacts/master/raw/pylint/pylint.log?job=pylint.

2. You can use GitLab's builtin badges instead of images in README

GitLab can now include badges in a projet or group, that will be displayed in the project header.

Got to Settings / General / Badges, then create a new badge by setting its link and image link, as described above:

screenshot of gitlab badges settings

Community
  • 1
  • 1
JGC
  • 5,725
  • 1
  • 32
  • 30
  • I just updated to include the pylint command, and sed command to extract the score. – JGC Dec 07 '17 at 21:26
  • Also worth mentioning that I wrote a little utility called [pylint-exit](https://github.com/jongracecox/pylint-exit) to handle pylint return codes properly. It's pretty neat, and worth using to translate the exit codes into a useful return code that you can used to decide whether to error your CI pipeline. – JGC Dec 07 '17 at 21:39
  • What happens if the the step fails? Then no badge is created (meaning that either no badge is available or the badge of the previous pipeline is still displayed). – tangoal Aug 18 '18 at 18:00
  • I also don't get the $file_list variable. Where does it come from? – tangoal Aug 18 '18 at 18:01
  • The $file_list variable is where you can add your list of files to run pylint against. I updated the example to clarify this. Also, if the step fails then no badge file will be generated. If the `pylint` job fails and the `pages` job in your CI pipeline runs then you will end up with missing image where badge is shown. If the `pages` step doesn't run then you will likely see the old badge. This will depend on whether you have `allow_failure: true` on your `pylint` job. – JGC Aug 20 '18 at 16:06
  • How to reference the badge? tryied https://gitlab.com/user/repo/badges/master/pylint.svg but is not working. – Jader Martins Jan 29 '19 at 18:48
  • You need to make sure the svg was picked up from the job that created it as an artifact. Go to the job and check the artifacts files. You should be able to see the file and download it. Next, make sure the file is put into a `public` folder before the pages job runs. Next, make sure the pages job ran. This should validate that the file made it to the right location and was published to pages. The URL should be something like: https://user.gitlab.io/pylint.svg. See https://docs.gitlab.com/ee/user/project/pages/getting_started_part_one.html#gitlab-pages-domain for more details. – JGC Jan 29 '19 at 19:57
  • [Jader](https://stackoverflow.com/users/7143310/jader-martins), I just updated my answer with some more details around this. – JGC Jan 29 '19 at 20:04
  • 4
    Sorry to bother, but is something wrong with the artifact urls? I can access them via the _job id_ (as zip or browsing) but it won't come around to let me url-fetch them via _master_. Just looking for confirmation, I keep trying. – Florian Heigl Jun 18 '20 at 23:09
  • 1
    Great answer. FYI since 2018 you can pass `--exit-zero` to pylint to force a 0 exit code. – Kirk Broadhurst Jul 13 '21 at 06:16
6

If you don't want to use the README, gitlab pages, anybadge or dropbox you can use https://img.shields.io/badge/lint%20score-$score-blue.svg to 'create' a badge (which is just an URL) and change the badge image URL via the gitlab API.

enter image description here

Details and the lint stage of my .gitlab-ci.yml

Manuel
  • 534
  • 3
  • 9
2

I developed a workaround solution to real-time per-job badges. It is not Pylint specific but the approach is general and you can easily modify it into what you need.

This example repo (branch badges) creates a custom badge for each CI job. There is also a complete walkthrough so I won't copy-paste it here.

The core idea is (assuming you're now inside a running CI job):

  • Create a badge (e.g. fetch it from shields.io into a file).
  • Upload the badge file to some real-time storage from where it can be linked (e.g. Dropbox).

Dropbox supports calling its API via HTTP requests (see this). Thus, all the above can be done using e.g. curl or Python requests - basic tools. You just need to pass the Dropbox access token as secret variable and save the file under the same name to overwrite the old badge.

Then, you can then directly link the Dropbox badge wherever you need. There are some tricks to it so be sure to check my example repo if you want to use it. For me it works quite well and seems to be fast.

The advantage of this method is that you don't have to mess with GitLab Pages. Instead of publishing on Pages you put it to Dropbox. That is a simple file transfer called by HTTP request. No more to that.

kubouch
  • 101
  • 1
  • 4
  • Is there a way to easily change the color of badges pulled from shields.io based on the score? I have used this approach in the past and had to include conditional logic in my CI to choose a color. An example of your code to pull the badge would be nice. – JGC Aug 20 '18 at 16:13
  • I like this approach. The reason I took my approach is because I'm using an internal Enterprise edition of GitLab using runners that don't have internet access. My approach means no outbound network connections. I would definitely recommend people consider your approach alongside mine . – JGC Jan 29 '19 at 20:09
  • @JGC Sorry for a late reply, frankly I've messed with badges only in one project since I wrote this :-D. The badge pulling code is in [update_badge.sh](https://gitlab.com/kubouch/ci-test/blob/badges/update_badge.sh). You can use any logic there - for example set badge parameters based on score, mine is just an example. For offline access you could also have a pre-fetched set of badges on your runner and then decide offline what you need. Or use your anybadge package - it all depends on one's preferences. – kubouch Jan 31 '19 at 08:46
  • 2
    As a side note, you can now add badges under your project/group description on GitLab: [Badges](https://gitlab.com/help/user/project/badges) – kubouch Jan 31 '19 at 08:58
1

Tutorial

  • Add the below file - .gitlab-ci.yml to your GitLab repository:

    pylint:
      stage: test
      image: python:3.7-slim
      before_script:
        - mkdir -p public/badges public/lint
        - echo undefined > public/badges/$CI_JOB_NAME.score
        - pip install pylint-gitlab
      script:
        - pylint --exit-zero --output-format=text $(find -type f -name "*.py" ! -path "**/.venv/**") | tee /tmp/pylint.txt
        - sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' /tmp/pylint.txt > public/badges/$CI_JOB_NAME.score
        - pylint --exit-zero --output-format=pylint_gitlab.GitlabCodeClimateReporter $(find -type f -name "*.py" ! -path "**/.venv/**") > codeclimate.json
        - pylint --exit-zero --output-format=pylint_gitlab.GitlabPagesHtmlReporter $(find -type f -name "*.py" ! -path "**/.venv/**") > public/lint/index.html
      after_script:
        - anybadge --overwrite --label $CI_JOB_NAME --value=$(cat public/badges/$CI_JOB_NAME.score) --file=public/badges/$CI_JOB_NAME.svg 4=red 6=orange 8=yellow 10=green
        - |
          echo "Your score is: $(cat public/badges/$CI_JOB_NAME.score)"
      artifacts:
        paths:
          - public
        reports:
          codequality: codeclimate.json
        when: always
    
    pages:
      stage: deploy
      image: alpine:latest
      script:
        - echo
      artifacts:
        paths:
          - public
      only:
        refs:
          - master
    

  • Substitute the below variables accordingly depending upon your project structure.
    For example, if your repository is located at company_intern/john/robot_code and you added the .gitlab-ci.yml file to your main branch, then:

    $GROUP = company_intern
    $SUBGROUP = john
    $PROJECT_NAME = robot_code
    $BRANCH = main
    
    # Substitute the above variables in the badge
    [![pylint](https://gitlab.com/$GROUP/$SUBGROUP/$PROJECT_NAME/-/jobs/artifacts/$BRANCH/raw/public/badges/pylint.svg?job=pylint)](https://gitlab.com/$GROUP/$SUBGROUP/$PROJECT_NAME/-/jobs/artifacts/$BRANCH/browse/public/lint?job=pylint)
    

  • Your badge has now been integrated!
    To verify the lining process locally before committing it directly:

    # To lint all the python files in the directory:
    pylint --exit-zero --output-format=text $(find -type f -name "*.py" ! -path "**/.venv/**")
    
    # To lint a specific file, say foo.py:
    pylint --exit-zero --output-format=text foo.py 
    

References:

0

If you use flake8 to run pylint, then an easy way to generate a badge is to use genbadge. This simple commandline tool provides capabilities to generate badges for tests, coverage, and flake8.

Simply run

genbadge flake8 -i flake8stats.txt

to generate the badge from a flake8 statistics file, such as this one: enter image description here. You can then use the badge to provide a quick link to the HTML report generated by flake8-html. See documentation for details (I'm the author by the way !).

smarie
  • 4,568
  • 24
  • 39