Extracting the dependency tree from Renovate for given repositories

Featured image for sharing metadata for article

As I wrote about in Analysing our dependency trees to determine where we should send Open Source contributions for Hacktoberfest I've been recently playing around Dependabot and the GitHub Advanced Security API, and using it both for managing maintenance of our dependencies, as well as getting better visibility across dependency usage.

One thing I've been finding with Dependabot is that it's leading to notification fatigue - partly due to being originally set up as daily updates (my bad!) but also because it doesn't allow workflows like combining PRs into one.

While talking about options to write some tooling to be able to do this for us, I took a step back to being able to leverage other tools for this purpose, instead of just Dependabot.

Fortunately I've used Renovate before for dependency management (and have some good tips for getting the most out of it) both professionally, and personally with my projects on GitLab.com. Renovate is much more fully-featured with the functionality it supports, allowing these workflows, among other great features.

(Fun aside - this is the first time I heard that WhiteSource, the company that runs Renovate, had been renamed Mend! I'll update the tags across my posts at a later point)

After getting it set up for trialing it on a few projects, I considered that retrieving the dependency graph should be possible, as the Renovate runner executes purely locally, so at some point the full dependency graph should be usable.

Looking around the documentation or renovate --help, I couldn't find anything, but eventually found this GitHub Discussion where someone had found a solution for doing this. Unhappy with this as a solution, last night I decided to play around with the Renovate code and see if I could get a raw export of the dependency graph Renovate sees.

The TL;DR is, yes you can, but it's a little hacky. You can find the project on GitLab.com.

Via the README, running the following:

npm exec tsup index.ts && node dist/index.js --token $GITHUB_COM_TOKEN jamietanna/jamietanna

Will create the file out/jamietanna-jamietanna.json, which contains the internal dependency data that Renovate sees, i.e.:

{
  "github-actions": [
    {
      "deps": [
        {
          "autoReplaceStringTemplate": "{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentDigest": "ec3a7ce113134d7a93b817d10a8272cb61118579",
          "datasource": "github-tags",
          "depIndex": 0,
          "depName": "actions/checkout",
          "depType": "action",
          "replaceString": "actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579",
          "updates": [
            {
              "branchName": "renovate/actions-checkout-digest",
              "newDigest": "1f9a0c22da41e6ebfa534300ef656657ea2c6707",
              "updateType": "digest"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        },
        {
          "autoReplaceStringTemplate": "{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentDigest": "c1aec4ac820532bab364f02a81873c555a0ba3a1",
          "datasource": "github-tags",
          "depIndex": 1,
          "depName": "ossf/scorecard-action",
          "depType": "action",
          "replaceString": "ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1",
          "updates": [
            {
              "branchName": "renovate/ossf-scorecard-action-digest",
              "newDigest": "066a051e5c2c336158e3c5728cd80ccb1276afbf",
              "updateType": "digest"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        },
        {
          "autoReplaceStringTemplate": "{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentDigest": "82c141cc518b40d92cc801eee768e7aafc9c2fa2",
          "datasource": "github-tags",
          "depIndex": 2,
          "depName": "actions/upload-artifact",
          "depType": "action",
          "replaceString": "actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2",
          "updates": [
            {
              "branchName": "renovate/actions-upload-artifact-digest",
              "newDigest": "83fd05a356d7e2593de66fc9913b3002723633cb",
              "updateType": "digest"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        },
        {
          "autoReplaceStringTemplate": "{{depName}}/upload-sarif@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentDigest": "5f532563584d71fdef14ee64d17bafb34f751ce5",
          "datasource": "github-tags",
          "depIndex": 3,
          "depName": "github/codeql-action",
          "depType": "action",
          "replaceString": "github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5",
          "updates": [
            {
              "branchName": "renovate/github-codeql-action-digest",
              "newDigest": "72bd9cbe6202944172589017f5da396f981101cf",
              "updateType": "digest"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        }
      ],
      "packageFile": ".github/workflows/main-scorecards-analysis.yml"
    },
    {
      "deps": [
        {
          "autoReplaceStringTemplate": "{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentValue": "v2",
          "currentVersion": "v2",
          "datasource": "github-tags",
          "depIndex": 0,
          "depName": "actions/setup-go",
          "depType": "action",
          "fixedVersion": "v2",
          "isSingleVersion": true,
          "replaceString": "actions/setup-go@v2",
          "sourceUrl": "https://github.com/actions/setup-go",
          "updates": [
            {
              "branchName": "renovate/actions-setup-go-3.x",
              "bucket": "major",
              "newMajor": 3,
              "newMinor": null,
              "newValue": "v3",
              "newVersion": "v3",
              "releaseTimestamp": "2022-10-17T16:33:22.000Z",
              "updateType": "major"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        },
        {
          "autoReplaceStringTemplate": "{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # tag={{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}",
          "commitMessageTopic": "{{{depName}}} action",
          "currentValue": "v2",
          "currentVersion": "v2",
          "datasource": "github-tags",
          "depIndex": 1,
          "depName": "actions/checkout",
          "depType": "action",
          "fixedVersion": "v2",
          "isSingleVersion": true,
          "replaceString": "actions/checkout@v2",
          "sourceUrl": "https://github.com/actions/checkout",
          "updates": [
            {
              "branchName": "renovate/actions-checkout-3.x",
              "bucket": "major",
              "newMajor": 3,
              "newMinor": null,
              "newValue": "v3",
              "newVersion": "v3",
              "releaseTimestamp": "2022-10-04T09:37:06.000Z",
              "updateType": "major"
            }
          ],
          "versioning": "docker",
          "warnings": [

          ]
        }
      ],
      "packageFile": ".github/workflows/rebuild.yml"
    }
  ]
}

From here we can see the depName, depType and packageFile as the key information here, but could also add other metadata as appropriate.

Alternatively, and handier for when running against quite a few repos, you can run it with --autodiscover:

# or with autodiscovery and a filter
npm exec tsup index.ts && node dist/index.js --token $GITHUB_COM_TOKEN --autodiscover --autodiscover-filter 'jamietanna/*'

Which then populates many files in the out directory.

As per the README of the project, you can then use sqlite-utils and a hand-rolled script to construct an SQLite database with the data, for easy querying.

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #whitesource-renovate #open-source #security #typescript.

Also on:

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.