As developers, we are constantly striving to write clean, maintainable code. But as codebases grow and evolve, it can be challenging to ensure consistency across the entire project. Consistent code formatting can make it easier to read and understand code, and can even reduce bugs and errors.
To address this issue, many teams use code formatters like clang-format to automatically enforce a consistent style across their codebase. But how can we ensure that our code is properly formatted before it even reaches the code review stage? This is where Bitbucket Pipelines comes in.
By integrating clang-format checks into your Bitbucket Pipeline, you can automatically test that your code is properly formatted on every push or pull request. This helps catch formatting errors early in the review process, making it easier to maintain a consistent codebase and ultimately reducing technical debt.
In this blog post, we’ll walk through how to set up a Bitbucket Pipeline step to test code formatting with clang-format. We’ll also discuss best practices for code formatting and how to integrate these checks into your development workflow. So, let’s get started!
Add a consistency check step to your pipeline
To test that formatting is correct with clang-format in a Bitbucket Pipeline, you can add a step to install clang-format and then run a check against your code files. Here’s an example of how you could do this:
definitions:
steps:
- step: &Check-Formatting
name: Check formatting
image: atlassian/default-image:4
script:
- apt-get update && apt-get install -y clang-format
- clang-format --version
- find . \( -name '*.h' -o -name '*.c' -o -name '*.hpp' -o -name '*.cpp' \) -exec clang-format -style=file --output-replacements-xml {} \; | grep "<replacement " && exit 1 || exit 0
pipelines:
pull-requests:
'**':
- step: *Check-Formatting
Code language: Lua (lua)
Here’s what this step does:
- Installs
clang-format
by runningapt-get update
andapt-get install -y clang-format
. - Prints the version of
clang-format
by runningclang-format --version
. This is useful for debugging and logging information in your pipeline. - Runs a find command to search the repository for all C/C++ source files (extensions
*.h
,*.c
,*.hpp
, and*.cpp
). If you have code in files with other extensions, you can add them to the search. Just add-o -name '*.<your extension>'
after the'*.cpp'
search term. Mind the space between the last term and the closing')'
! - Runs
clang-format
against the found code files with the-style=file
flag, which tellsclang-format
to use the formatting style specified in the.clang-format
file in the root of your repository. The command generates an XML report of the formatting changes. - Pipes the XML report to
grep "<replacement "
, which searches for any lines in the report that contain the<replacement>
tag. This tag indicates thatclang-format
made a formatting change to the code. If any replacements are found, the pipeline will exit with an error code (exit 1
). Otherwise, it will exit successfully (exit 0
).
The pipeline step will pass if this returns with exit code of 0 and fail otherwise. Place this step as the first step in your pull request pipeline and when a pull request is opened it will fail quickly if there are formatting issues. This indicates to the developer they need to fix those issues before continuing.
Note that this checks the entire repository, not just the changes!
Best Formatting Practices
Ensuring consistent formatting is definitely a good practice to work into your development practice. To make it easy for developers to do this, many IDEs support formatting files per some standard. For example, Eclipse allows you to define a style and easily format highlighted sections or the entire file. VS Code has options to format your code when you save a file, in addition to formatting specific sections of code you are working on.
Outside of your IDE, my favorite option is to use a git hook to format your patches as you commit them to the repository. By using the pre-commit hook and some clang-format scripts, you can ensure that any new code or changed code gets formatted properly before getting pushed to the server. Read more about how to set that up in my other post.
Best practices for enforcing consistent formatting comes down to two approaches, in my opinion, and depends on whether you are working with an established code base or a new one.
Consistent Formatting for Established Code Base
For established code bases, it may not be desirable or feasible to format all the code. That would require a huge amount of retest and revalidation that just may not be possible. In that case, set up your pipeline to just check the formatting on patches in the pull request. As developers make changes to fix bugs or add features, those new changes will go through the testing and validation process and also get the correct formatting applied. Over time, the consistency of the formatting will increase as more and more of the code gets updated.
To help developers automate this, set up the development environment to format changes with a git pre-commit hook. This will cut down on pipeline failures which enforce that patches are formatted properly on pull requests.
An example pipeline that enforces formatting on just patches is shown here:
definitions:
steps:
- step: &Check-Patch-Formatting
name: Check code formatting with clang-format-diff
image: atlassian/default-image:4
script:
# Install clang-format-diff
- apt-get update && apt-get install -y clang-format
# Run clang-format-diff on modified files
- if [[ $(git diff -U0 --no-color --relative origin/master...origin/${BITBUCKET_BRANCH} | clang-format-diff -p1) ]]; then exit 1; fi
pipelines:
pull-requests:
'**':
- step: *Check-Patch-Formatting
Code language: PHP (php)
Consistent Formatting for New Code Base
When working with a new project and a new code base, there is no reason why you can’t enforce formatting right off the bat.
First, set up your IDE to format your files on save, to ensure they stay properly formatted. In addition, ensure you have the pre-commit hook installed to validate formatting (and to fix it) before committing. Finally, set up the pipeline with two steps: one for validating the patch on each PR, and another that checks the entire code base as part of a nightly (or weekly) build process. The first pipeline in this post shows this type of pipeline step (for a pull-request).
Conclusion
In conclusion, maintaining consistent code formatting is crucial for writing readable, maintainable code. With Bitbucket Pipelines and clang-format, we can automate the process of checking our code formatting and catch errors before they make it into production. By setting up a simple Pipeline step to run clang-format checks, we can ensure that our codebase stays tidy and easy to read.
Remember that consistent code formatting is just one piece of the puzzle when it comes to writing high-quality code. It’s important to also focus on good design principles, writing clear and concise code, and properly testing our applications. By making conscious decisions and striving to improve our development practices, we can create software that is not only functional, but also maintainable and sustainable in the long run.
Additional Resources
- Bitbucket Pipelines documentation: The official documentation provides a detailed guide on setting up and using Pipelines, including how to integrate with external tools like clang-format.
- clang-format documentation: The official documentation provides a comprehensive guide on using clang-format to format code, including style options and command line usage.
- GitHub Action for clang-format: If you use GitHub instead of Bitbucket, you can check out this GitHub Action which can help you integrate clang-format checks into your workflow.
- Better coding practices for your organisation using clang-tidy and clang-format: This Medium post provides a practical guide to using clang-tidy and clang-format to format C++ code in a consistent and effective manner.
Comments