“Surfacing Pull Request hidden changes”, but for lazy people

Mateusz Kwieciński
4 min readSep 3, 2020

--

a simple guide to integrate Jake Wharton’s dependency-tree-diff library using Github Actions

Quick links: 👀
dependency-tree-diff-action on Github Actions Marketplace
Sample pull request and sample workflow that shows it in action, as a Pull Request comment

Part of dependency diff when downgrading lifecycle library

dependency-tree-diff tool

Quite recently Jake Wharton shared another extremely useful library that helps developers in their day-to-day activities. This time, the focus went on being aware of hidden changes made by developers during regular development process.
Not to duplicate the original article: Surfacing Hidden Change to Pull Requests, long story short, a dependency-tree-diff library has been released. Again, its documentation is self-explanatory, and it is highly recommended to go through it to fully grasp what kind of problem it tries to adress.

Use Github Actions

The only remaining challenge is to integrate the tool into the process to see the diff on every Pull Request.
That’s exactly the reason why a dependency-tree-diff-action has been created. The goal was to make it super easy to see a dependency diff when using Github Actions.

The action itself can be applied using:

- id: dependency-diff
name: Generate dependency diff
uses: usefulness/dependency-tree-diff-action@v1

which then exposes an output with the diff ${{ steps.dependency-diff.outputs.text-diff }} that can be consumed later. The diff will be calculated between the currently checked branch, and the base branch the pull request targets to.

The configuration can be customized accordingly to the project setup so, being short, one should provide the project and its configuration that should have the diff generated. For example, for Android project one should provide the module that has the com.android.application plugin applied and configuration related to the released artifact. All available configurations can be viewed using./gradlew app:dependencies task. (adding a | grep RuntimeClasspath should help and narrow the results)

Project configuration can be provided to the action using its inputs:

- id: dependency-diff
name: Generate dependency diff
uses: usefulness/dependency-tree-diff-action@v1
with:
configuration: 'releaseRuntimeClasspath'
project: 'app'
build-root-directory: .
lib-version: '1.1.0'

Further Integration

Having a diff in a build variable allows consuming it according to one’s needs. As an example it can be simply printed as a Pull Request comment using create-or-update-comment action.

- uses: peter-evans/create-or-update-comment@v1
with:
body: |
Dependency diff:
```diff
${{ steps.dependency-diff.outputs.text-diff }}
```
edit-mode: replace
issue-number: ${{ github.event.pull_request.number }}
token: ${{ secrets.GITHUB_TOKEN }}

moreover, to avoid unnecessary noise in Pull Request comments, let’s improve the workflow by:

  1. Updating the comment on each build instead of creating a new one on each build
  2. Creating the comment only when the diff actually changes.
- uses: peter-evans/find-comment@v1
id: find_comment
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Dependency diff
- uses: peter-evans/create-or-update-comment@v1
if: ${{ steps.dependency-diff.outputs.text-diff != null || steps.find_comment.outputs.comment-id != null }}
with:
body: |
Dependency diff (customize your message here):
```diff
${{ steps.dependency-diff.outputs.text-diff }}
```
edit-mode: replace
comment-id: ${{ steps.find_comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
token: ${{ secrets.GITHUB_TOKEN }}

see full workflow definition in plugin’s documentation or at sample repository

When everyting is properly set up, the expected result is a pull request comment whenever the dependency graph changes.

Internals

dependency-tree-diff binary expects 2 dependency graphs passed. For a pull request that would be dependency tree at the branch's HEAD and graph at Pull Request target branch. The action expects to be called only on Pull Requests as a trigger which allows safely assume${{ github.base_ref }} build variable has a non null value.
In the simplest form action's workflow comes down to:

  1. Generate dependency graph for currently checked commit and save it to a temp file
  2. Check out ${{ github.base_ref }} branch
  3. Generate dependency graph for the checked commit and save it to a temp file
  4. Run dependency-tree-diff binary and store its output in a Github Action's variable

Another thing worth mentioning is how the dependency graph gets generated. Having simplicity as the main point the action, being at first version the action assumes it can use Gradle wrapper instance committed to the repository, directly calling ./gradlew script.

Summary

Despite few limitations described action should already be useful, but as always, contributions making it tailored to any other use case are welcome.
Hopefully, even in more advanced cases, this article will bring a few ideas how to unveil hidden changes and make use of great tools shared by other people.

Happy coding!

--

--