From 8a7abfdf01cb768817ec5a400732c56698138dec Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Sun, 13 Feb 2022 06:24:40 +0300
Subject: [PATCH] refactor(github): Convert datasources to class form (#14124)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Jamie Magee <jamie.magee@gmail.com>
---
 lib/constants/platform.spec.ts                |  12 +-
 lib/datasource/api.ts                         |   8 +-
 lib/datasource/github-releases/common.spec.ts |  17 +-
 lib/datasource/github-releases/common.ts      |  20 +
 lib/datasource/github-releases/digest.spec.ts |  51 +-
 lib/datasource/github-releases/index.spec.ts  |  20 +-
 lib/datasource/github-releases/index.ts       | 466 ++++++++----------
 lib/datasource/github-tags/index.spec.ts      |   4 +-
 lib/datasource/github-tags/index.ts           | 290 +++++------
 .../go/__snapshots__/index.spec.ts.snap       |  10 -
 lib/datasource/go/base.spec.ts                |   6 +-
 lib/datasource/go/base.ts                     |  10 +-
 lib/datasource/go/common.ts                   |   6 +-
 lib/datasource/go/index.spec.ts               |  24 +-
 lib/datasource/go/index.ts                    |   6 +-
 lib/datasource/go/releases-direct.ts          |   8 +-
 lib/manager/ansible-galaxy/collections.ts     |   4 +-
 lib/manager/ansible-galaxy/index.ts           |   4 +-
 lib/manager/batect-wrapper/extract.spec.ts    |   6 +-
 lib/manager/batect-wrapper/extract.ts         |   4 +-
 lib/manager/batect-wrapper/index.ts           |   4 +-
 lib/manager/bazel/extract.ts                  |  10 +-
 lib/manager/bazel/index.ts                    |   8 +-
 lib/manager/buildkite/extract.ts              |   6 +-
 lib/manager/buildkite/index.ts                |   4 +-
 lib/manager/cocoapods/extract.ts              |   4 +-
 lib/manager/cocoapods/index.ts                |   4 +-
 lib/manager/flux/extract.ts                   |   4 +-
 lib/manager/flux/index.ts                     |   7 +-
 lib/manager/github-actions/extract.ts         |   4 +-
 lib/manager/github-actions/index.ts           |   4 +-
 lib/manager/homebrew/extract.ts               |   4 +-
 lib/manager/homebrew/index.ts                 |   4 +-
 lib/manager/kustomize/extract.spec.ts         |  10 +-
 lib/manager/kustomize/extract.ts              |   4 +-
 lib/manager/kustomize/index.ts                |   4 +-
 lib/manager/nodenv/extract.ts                 |   4 +-
 lib/manager/nodenv/index.ts                   |   4 +-
 lib/manager/npm/extract/index.ts              |  12 +-
 lib/manager/npm/index.ts                      |   4 +-
 lib/manager/nvm/extract.ts                    |   4 +-
 lib/manager/nvm/index.ts                      |   4 +-
 lib/manager/pre-commit/extract.ts             |   6 +-
 lib/manager/pre-commit/index.ts               |   7 +-
 lib/manager/terraform-version/extract.ts      |   4 +-
 lib/manager/terraform-version/index.ts        |   4 +-
 lib/manager/terraform/index.ts                |   4 +-
 lib/manager/terraform/modules.ts              |   4 +-
 lib/manager/terraform/required-version.ts     |   4 +-
 lib/manager/terragrunt-version/extract.ts     |   4 +-
 lib/manager/terragrunt-version/index.ts       |   4 +-
 lib/manager/terragrunt/index.ts               |   4 +-
 lib/manager/terragrunt/modules.ts             |   4 +-
 lib/manager/travis/extract.ts                 |   8 +-
 lib/manager/travis/index.ts                   |   4 +-
 lib/util/http/github.spec.ts                  |   6 +-
 .../repository/process/lookup/index.spec.ts   |  84 ++--
 57 files changed, 594 insertions(+), 650 deletions(-)
 create mode 100644 lib/datasource/github-releases/common.ts

diff --git a/lib/constants/platform.spec.ts b/lib/constants/platform.spec.ts
index 24bd14b7ae..321b991aab 100644
--- a/lib/constants/platform.spec.ts
+++ b/lib/constants/platform.spec.ts
@@ -1,6 +1,6 @@
 import { BitBucketTagsDatasource } from '../datasource/bitbucket-tags';
-import { id as GH_RELEASES_DS } from '../datasource/github-releases';
-import { id as GH_TAGS_DS } from '../datasource/github-tags';
+import { GithubReleasesDatasource } from '../datasource/github-releases';
+import { GithubTagsDatasource } from '../datasource/github-tags';
 import { GitlabPackagesDatasource } from '../datasource/gitlab-packages';
 import { GitlabReleasesDatasource } from '../datasource/gitlab-releases';
 import { GitlabTagsDatasource } from '../datasource/gitlab-tags';
@@ -36,8 +36,12 @@ describe('constants/platform', () => {
   });
 
   it('should be part of the GITHUB_API_USING_HOST_TYPES ', () => {
-    expect(GITHUB_API_USING_HOST_TYPES.includes(GH_TAGS_DS)).toBeTrue();
-    expect(GITHUB_API_USING_HOST_TYPES.includes(GH_RELEASES_DS)).toBeTrue();
+    expect(
+      GITHUB_API_USING_HOST_TYPES.includes(GithubTagsDatasource.id)
+    ).toBeTrue();
+    expect(
+      GITHUB_API_USING_HOST_TYPES.includes(GithubReleasesDatasource.id)
+    ).toBeTrue();
     expect(GITHUB_API_USING_HOST_TYPES.includes(PodDatasource.id)).toBeTrue();
     expect(
       GITHUB_API_USING_HOST_TYPES.includes(GITHUB_CHANGELOG_ID)
diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts
index 11d8aaae45..0d23685404 100644
--- a/lib/datasource/api.ts
+++ b/lib/datasource/api.ts
@@ -12,8 +12,8 @@ import { GalaxyDatasource } from './galaxy';
 import { GalaxyCollectionDatasource } from './galaxy-collection';
 import { GitRefsDatasource } from './git-refs';
 import { GitTagsDatasource } from './git-tags';
-import * as githubReleases from './github-releases';
-import * as githubTags from './github-tags';
+import { GithubReleasesDatasource } from './github-releases';
+import { GithubTagsDatasource } from './github-tags';
 import { GitlabPackagesDatasource } from './gitlab-packages';
 import { GitlabReleasesDatasource } from './gitlab-releases';
 import { GitlabTagsDatasource } from './gitlab-tags';
@@ -57,8 +57,8 @@ api.set(GalaxyDatasource.id, new GalaxyDatasource());
 api.set(GalaxyCollectionDatasource.id, new GalaxyCollectionDatasource());
 api.set(GitRefsDatasource.id, new GitRefsDatasource());
 api.set(GitTagsDatasource.id, new GitTagsDatasource());
-api.set('github-releases', githubReleases);
-api.set('github-tags', githubTags);
+api.set(GithubReleasesDatasource.id, new GithubReleasesDatasource());
+api.set(GithubTagsDatasource.id, new GithubTagsDatasource());
 api.set(GitlabPackagesDatasource.id, new GitlabPackagesDatasource());
 api.set(GitlabReleasesDatasource.id, new GitlabReleasesDatasource());
 api.set(GitlabTagsDatasource.id, new GitlabTagsDatasource());
diff --git a/lib/datasource/github-releases/common.spec.ts b/lib/datasource/github-releases/common.spec.ts
index 352dc80df7..bdf3b4443a 100644
--- a/lib/datasource/github-releases/common.spec.ts
+++ b/lib/datasource/github-releases/common.spec.ts
@@ -1,5 +1,4 @@
-import { GitHubReleaseMocker } from './test';
-import { getApiBaseUrl, getGithubRelease, getSourceUrlBase } from '.';
+import { getApiBaseUrl, getSourceUrlBase } from './common';
 
 describe('datasource/github-releases/common', () => {
   describe('getSourceUrlBase', () => {
@@ -24,18 +23,4 @@ describe('datasource/github-releases/common', () => {
       expect(apiUrl).toBe('https://gh.my-company.com/api/v3/');
     });
   });
-
-  describe('getGithubRelease', () => {
-    const apiUrl = 'https://github.com/';
-    const lookupName = 'someDep';
-    const releaseMock = new GitHubReleaseMocker(apiUrl, lookupName);
-
-    it('returns release', async () => {
-      const version = 'v1.0.0';
-      releaseMock.release(version);
-
-      const release = await getGithubRelease(apiUrl, lookupName, version);
-      expect(release.tag_name).toBe(version);
-    });
-  });
 });
diff --git a/lib/datasource/github-releases/common.ts b/lib/datasource/github-releases/common.ts
new file mode 100644
index 0000000000..0ec694dd97
--- /dev/null
+++ b/lib/datasource/github-releases/common.ts
@@ -0,0 +1,20 @@
+import { ensureTrailingSlash } from '../../util/url';
+
+const defaultSourceUrlBase = 'https://github.com/';
+
+export function getSourceUrlBase(registryUrl: string): string {
+  // default to GitHub.com if no GHE host is specified.
+  return ensureTrailingSlash(registryUrl ?? defaultSourceUrlBase);
+}
+
+export function getApiBaseUrl(registryUrl: string): string {
+  const sourceUrlBase = getSourceUrlBase(registryUrl);
+  return sourceUrlBase === defaultSourceUrlBase
+    ? `https://api.github.com/`
+    : `${sourceUrlBase}api/v3/`;
+}
+
+export function getSourceUrl(lookupName: string, registryUrl?: string): string {
+  const sourceUrlBase = getSourceUrlBase(registryUrl);
+  return `${sourceUrlBase}${lookupName}`;
+}
diff --git a/lib/datasource/github-releases/digest.spec.ts b/lib/datasource/github-releases/digest.spec.ts
index 56f4f27ebb..7ffd8ade6d 100644
--- a/lib/datasource/github-releases/digest.spec.ts
+++ b/lib/datasource/github-releases/digest.spec.ts
@@ -2,7 +2,8 @@ import hasha from 'hasha';
 import * as httpMock from '../../../test/http-mock';
 import { GitHubReleaseMocker } from './test';
 import type { DigestAsset } from './types';
-import { findDigestAsset, mapDigestAssetToRelease } from '.';
+
+import { GithubReleasesDatasource } from '.';
 
 describe('datasource/github-releases/digest', () => {
   const lookupName = 'some/dep';
@@ -10,6 +11,7 @@ describe('datasource/github-releases/digest', () => {
     'https://api.github.com',
     lookupName
   );
+  const githubReleases = new GithubReleasesDatasource();
 
   describe('findDigestAsset', () => {
     it('finds SHASUMS.txt file containing digest', async () => {
@@ -19,7 +21,10 @@ describe('datasource/github-releases/digest', () => {
         'another-digest linux-arm64.tar.gz'
       );
 
-      const digestAsset = await findDigestAsset(release, 'test-digest');
+      const digestAsset = await githubReleases.findDigestAsset(
+        release,
+        'test-digest'
+      );
       expect(digestAsset.assetName).toBe('SHASUMS.txt');
       expect(digestAsset.digestedFileName).toBe('linux-amd64.tar.gz');
     });
@@ -35,7 +40,10 @@ describe('datasource/github-releases/digest', () => {
         .get(`/repos/${lookupName}/releases/download/v1.0.0/SHASUMS.txt`)
         .reply(200, '');
 
-      const digestAsset = await findDigestAsset(release, 'test-digest');
+      const digestAsset = await githubReleases.findDigestAsset(
+        release,
+        'test-digest'
+      );
       expect(digestAsset).toBeNull();
     });
 
@@ -49,14 +57,20 @@ describe('datasource/github-releases/digest', () => {
       });
       const contentDigest = await hasha.async(content, { algorithm: 'sha256' });
 
-      const digestAsset = await findDigestAsset(release, contentDigest);
+      const digestAsset = await githubReleases.findDigestAsset(
+        release,
+        contentDigest
+      );
       expect(digestAsset.assetName).toBe('asset.zip');
       expect(digestAsset.digestedFileName).toBeUndefined();
     });
 
     it('returns null when no assets available', async () => {
       const release = releaseMock.release('v1.0.0');
-      const digestAsset = await findDigestAsset(release, 'test-digest');
+      const digestAsset = await githubReleases.findDigestAsset(
+        release,
+        'test-digest'
+      );
       expect(digestAsset).toBeNull();
     });
   });
@@ -75,7 +89,10 @@ describe('datasource/github-releases/digest', () => {
           'v1.0.1',
           'updated-digest  asset.zip'
         );
-        const digest = await mapDigestAssetToRelease(digestAsset, release);
+        const digest = await githubReleases.mapDigestAssetToRelease(
+          digestAsset,
+          release
+        );
         expect(digest).toBe('updated-digest');
       });
 
@@ -89,7 +106,7 @@ describe('datasource/github-releases/digest', () => {
           'v1.0.1',
           'updated-digest  asset-1.0.1.zip'
         );
-        const digest = await mapDigestAssetToRelease(
+        const digest = await githubReleases.mapDigestAssetToRelease(
           digestAssetWithVersion,
           release
         );
@@ -101,13 +118,19 @@ describe('datasource/github-releases/digest', () => {
           'v1.0.1',
           'moot-digest asset.tar.gz'
         );
-        const digest = await mapDigestAssetToRelease(digestAsset, release);
+        const digest = await githubReleases.mapDigestAssetToRelease(
+          digestAsset,
+          release
+        );
         expect(digest).toBeNull();
       });
 
       it('returns null when digest file not found', async () => {
         const release = releaseMock.release('v1.0.1');
-        const digest = await mapDigestAssetToRelease(digestAsset, release);
+        const digest = await githubReleases.mapDigestAssetToRelease(
+          digestAsset,
+          release
+        );
         expect(digest).toBeNull();
       });
     });
@@ -128,13 +151,19 @@ describe('datasource/github-releases/digest', () => {
           algorithm: 'sha256',
         });
 
-        const digest = await mapDigestAssetToRelease(digestAsset, release);
+        const digest = await githubReleases.mapDigestAssetToRelease(
+          digestAsset,
+          release
+        );
         expect(digest).toEqual(contentDigest);
       });
 
       it('returns null when not found', async () => {
         const release = releaseMock.release('v1.0.1');
-        const digest = await mapDigestAssetToRelease(digestAsset, release);
+        const digest = await githubReleases.mapDigestAssetToRelease(
+          digestAsset,
+          release
+        );
         expect(digest).toBeNull();
       });
     });
diff --git a/lib/datasource/github-releases/index.spec.ts b/lib/datasource/github-releases/index.spec.ts
index 786b08b510..4e74c0fbe9 100644
--- a/lib/datasource/github-releases/index.spec.ts
+++ b/lib/datasource/github-releases/index.spec.ts
@@ -2,8 +2,7 @@ import { getDigest, getPkgReleases } from '..';
 import * as httpMock from '../../../test/http-mock';
 import * as _hostRules from '../../util/host-rules';
 import { GitHubReleaseMocker } from './test';
-import { id as datasource } from '.';
-import * as github from '.';
+import { GithubReleasesDatasource } from '.';
 
 jest.mock('../../util/host-rules');
 const hostRules: any = _hostRules;
@@ -25,6 +24,8 @@ const responseBody = [
 ];
 
 describe('datasource/github-releases/index', () => {
+  const githubReleases = new GithubReleasesDatasource();
+
   beforeEach(() => {
     hostRules.hosts.mockReturnValue([]);
     hostRules.find.mockReturnValue({
@@ -40,7 +41,7 @@ describe('datasource/github-releases/index', () => {
         .reply(200, responseBody);
 
       const res = await getPkgReleases({
-        datasource,
+        datasource: GithubReleasesDatasource.id,
         depName: 'some/dep',
       });
       expect(res).toMatchSnapshot();
@@ -62,7 +63,7 @@ describe('datasource/github-releases/index', () => {
         .scope(githubEnterpriseApiHost)
         .get(`/api/v3/repos/${lookupName}/releases?per_page=100`)
         .reply(200, responseBody);
-      const res = await github.getReleases({
+      const res = await githubReleases.getReleases({
         registryUrl: 'https://git.enterprise.com',
         lookupName,
       });
@@ -80,14 +81,17 @@ describe('datasource/github-releases/index', () => {
     const releaseMock = new GitHubReleaseMocker(githubApiHost, depName);
 
     it('requires currentDigest', async () => {
-      const digest = await getDigest({ datasource, depName }, currentValue);
+      const digest = await getDigest(
+        { datasource: GithubReleasesDatasource.id, depName },
+        currentValue
+      );
       expect(digest).toBeNull();
     });
 
     it('defaults to currentDigest when currentVersion is missing', async () => {
       const digest = await getDigest(
         {
-          datasource,
+          datasource: GithubReleasesDatasource.id,
           depName,
           currentDigest,
         },
@@ -106,7 +110,7 @@ describe('datasource/github-releases/index', () => {
       releaseMock.withDigestFileAsset(nextValue, `${nextDigest} asset.zip`);
       const digest = await getDigest(
         {
-          datasource,
+          datasource: GithubReleasesDatasource.id,
           depName,
           currentValue,
           currentDigest,
@@ -122,7 +126,7 @@ describe('datasource/github-releases/index', () => {
       releaseMock.release(currentValue);
       const digest = await getDigest(
         {
-          datasource,
+          datasource: GithubReleasesDatasource.id,
           depName,
           currentValue,
           currentDigest,
diff --git a/lib/datasource/github-releases/index.ts b/lib/datasource/github-releases/index.ts
index 4b7f95c2bd..3d2088156c 100644
--- a/lib/datasource/github-releases/index.ts
+++ b/lib/datasource/github-releases/index.ts
@@ -1,45 +1,14 @@
 import hasha from 'hasha';
 import { logger } from '../../logger';
-import * as packageCache from '../../util/cache/package';
+import { cache } from '../../util/cache/package/decorator';
 import { GithubHttp } from '../../util/http/github';
 import { newlineRegex, regEx } from '../../util/regex';
-import { ensureTrailingSlash } from '../../util/url';
+import { Datasource } from '../datasource';
 import type { DigestConfig, GetReleasesConfig, ReleaseResult } from '../types';
+import { getApiBaseUrl, getSourceUrl } from './common';
 import type { DigestAsset, GithubRelease, GithubReleaseAsset } from './types';
 
-export const customRegistrySupport = true;
-export const defaultRegistryUrls = ['https://github.com'];
-export const registryStrategy = 'first';
-
-const defaultSourceUrlBase = 'https://github.com/';
-export const id = 'github-releases';
-
 export const cacheNamespace = 'datasource-github-releases';
-export const http = new GithubHttp(id);
-
-async function findDigestFile(
-  release: GithubRelease,
-  digest: string
-): Promise<DigestAsset | null> {
-  const smallAssets = release.assets.filter(
-    (a: GithubReleaseAsset) => a.size < 5 * 1024
-  );
-  for (const asset of smallAssets) {
-    const res = await http.get(asset.browser_download_url);
-    for (const line of res.body.split(newlineRegex)) {
-      const [lineDigest, lineFn] = line.split(regEx(/\s+/), 2);
-      if (lineDigest === digest) {
-        return {
-          assetName: asset.name,
-          digestedFileName: lineFn,
-          currentVersion: release.tag_name,
-          currentDigest: lineDigest,
-        };
-      }
-    }
-  }
-  return null;
-}
 
 function inferHashAlg(digest: string): string {
   switch (digest.length) {
@@ -51,243 +20,240 @@ function inferHashAlg(digest: string): string {
   }
 }
 
-function getAssetDigestCacheKey(
-  downloadUrl: string,
-  algorithm: string
-): string {
-  const type = 'assetDigest';
-  return `${downloadUrl}:${algorithm}:${type}`;
-}
+export class GithubReleasesDatasource extends Datasource {
+  static id = 'github-releases';
 
-async function downloadAndDigest(
-  asset: GithubReleaseAsset,
-  algorithm: string
-): Promise<string> {
-  const downloadUrl = asset.browser_download_url;
-  const cacheKey = getAssetDigestCacheKey(downloadUrl, algorithm);
-  const cachedResult = await packageCache.get<string>(cacheNamespace, cacheKey);
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
-  }
+  override readonly defaultRegistryUrls = ['https://github.com'];
 
-  const res = http.stream(downloadUrl);
-  const digest = await hasha.fromStream(res, { algorithm });
+  override http: GithubHttp;
 
-  const cacheMinutes = 1440;
-  await packageCache.set(cacheNamespace, cacheKey, digest, cacheMinutes);
-  return digest;
-}
+  constructor(id = GithubReleasesDatasource.id) {
+    super(id);
+    this.http = new GithubHttp(id);
+  }
 
-async function findAssetWithDigest(
-  release: GithubRelease,
-  digest: string
-): Promise<DigestAsset | null> {
-  const algorithm = inferHashAlg(digest);
-  const assetsBySize = release.assets.sort(
-    (a: GithubReleaseAsset, b: GithubReleaseAsset) => {
-      if (a.size < b.size) {
-        return -1;
-      }
-      if (a.size > b.size) {
-        return 1;
+  async findDigestFile(
+    release: GithubRelease,
+    digest: string
+  ): Promise<DigestAsset | null> {
+    const smallAssets = release.assets.filter(
+      (a: GithubReleaseAsset) => a.size < 5 * 1024
+    );
+    for (const asset of smallAssets) {
+      const res = await this.http.get(asset.browser_download_url);
+      for (const line of res.body.split(newlineRegex)) {
+        const [lineDigest, lineFn] = line.split(regEx(/\s+/), 2);
+        if (lineDigest === digest) {
+          return {
+            assetName: asset.name,
+            digestedFileName: lineFn,
+            currentVersion: release.tag_name,
+            currentDigest: lineDigest,
+          };
+        }
       }
-      return 0;
-    }
-  );
-
-  for (const asset of assetsBySize) {
-    const assetDigest = await downloadAndDigest(asset, algorithm);
-    if (assetDigest === digest) {
-      return {
-        assetName: asset.name,
-        currentVersion: release.tag_name,
-        currentDigest: assetDigest,
-      };
     }
+    return null;
   }
-  return null;
-}
 
-/** Identify the asset associated with a known digest. */
-export async function findDigestAsset(
-  release: GithubRelease,
-  digest: string
-): Promise<DigestAsset> {
-  const digestFile = await findDigestFile(release, digest);
-  if (digestFile) {
-    return digestFile;
+  @cache({
+    ttlMinutes: 1440,
+    namespace: 'datasource-github-releases',
+    key: (asset: GithubReleaseAsset, algorithm: string) =>
+      `${asset.browser_download_url}:${algorithm}:assetDigest`,
+  })
+  async downloadAndDigest(
+    asset: GithubReleaseAsset,
+    algorithm: string
+  ): Promise<string> {
+    const res = this.http.stream(asset.browser_download_url);
+    const digest = await hasha.fromStream(res, { algorithm });
+    return digest;
   }
 
-  const asset = await findAssetWithDigest(release, digest);
-  return asset;
-}
+  async findAssetWithDigest(
+    release: GithubRelease,
+    digest: string
+  ): Promise<DigestAsset | null> {
+    const algorithm = inferHashAlg(digest);
+    const assetsBySize = release.assets.sort(
+      (a: GithubReleaseAsset, b: GithubReleaseAsset) => {
+        if (a.size < b.size) {
+          return -1;
+        }
+        if (a.size > b.size) {
+          return 1;
+        }
+        return 0;
+      }
+    );
 
-/** Given a digest asset, find the equivalent digest in a different release. */
-export async function mapDigestAssetToRelease(
-  digestAsset: DigestAsset,
-  release: GithubRelease
-): Promise<string | null> {
-  const current = digestAsset.currentVersion.replace(regEx(/^v/), '');
-  const next = release.tag_name.replace(regEx(/^v/), '');
-  const releaseChecksumAssetName = digestAsset.assetName.replace(current, next);
-  const releaseAsset = release.assets.find(
-    (a: GithubReleaseAsset) => a.name === releaseChecksumAssetName
-  );
-  if (!releaseAsset) {
-    return null;
-  }
-  if (digestAsset.digestedFileName) {
-    const releaseFilename = digestAsset.digestedFileName.replace(current, next);
-    const res = await http.get(releaseAsset.browser_download_url);
-    for (const line of res.body.split(newlineRegex)) {
-      const [lineDigest, lineFn] = line.split(regEx(/\s+/), 2);
-      if (lineFn === releaseFilename) {
-        return lineDigest;
+    for (const asset of assetsBySize) {
+      const assetDigest = await this.downloadAndDigest(asset, algorithm);
+      if (assetDigest === digest) {
+        return {
+          assetName: asset.name,
+          currentVersion: release.tag_name,
+          currentDigest: assetDigest,
+        };
       }
     }
-  } else {
-    const algorithm = inferHashAlg(digestAsset.currentDigest);
-    const newDigest = await downloadAndDigest(releaseAsset, algorithm);
-    return newDigest;
+    return null;
   }
-  return null;
-}
-
-export function getSourceUrlBase(registryUrl: string): string {
-  // default to GitHub.com if no GHE host is specified.
-  return ensureTrailingSlash(registryUrl ?? defaultSourceUrlBase);
-}
-
-export function getApiBaseUrl(registryUrl: string): string {
-  const sourceUrlBase = getSourceUrlBase(registryUrl);
-  return sourceUrlBase === defaultSourceUrlBase
-    ? `https://api.github.com/`
-    : `${sourceUrlBase}api/v3/`;
-}
 
-export function getSourceUrl(lookupName: string, registryUrl?: string): string {
-  const sourceUrlBase = getSourceUrlBase(registryUrl);
-  return `${sourceUrlBase}${lookupName}`;
-}
-
-export async function getGithubRelease(
-  apiBaseUrl: string,
-  repo: string,
-  version: string
-): Promise<GithubRelease> {
-  const url = `${apiBaseUrl}repos/${repo}/releases/tags/${version}`;
-  const res = await http.getJson<GithubRelease>(url);
-  return res.body;
-}
-
-function getReleasesCacheKey(registryUrl: string, repo: string): string {
-  const type = 'tags';
-  return `${registryUrl}:${repo}:${type}`;
-}
+  /** Identify the asset associated with a known digest. */
+  async findDigestAsset(
+    release: GithubRelease,
+    digest: string
+  ): Promise<DigestAsset> {
+    const digestFile = await this.findDigestFile(release, digest);
+    if (digestFile) {
+      return digestFile;
+    }
 
-/**
- * github.getReleases
- *
- * This function can be used to fetch releases with a customisable versioning (e.g. semver) and with releases.
- *
- * This function will:
- *  - Fetch all releases
- *  - Sanitize the versions if desired (e.g. strip out leading 'v')
- *  - Return a dependency object containing sourceUrl string and releases array
- */
-export async function getReleases({
-  lookupName: repo,
-  registryUrl,
-}: GetReleasesConfig): Promise<ReleaseResult | null> {
-  const cacheKey = getReleasesCacheKey(registryUrl, repo);
-  const cachedResult = await packageCache.get<ReleaseResult>(
-    cacheNamespace,
-    cacheKey
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
+    const asset = await this.findAssetWithDigest(release, digest);
+    return asset;
   }
-  const apiBaseUrl = getApiBaseUrl(registryUrl);
-  const url = `${apiBaseUrl}repos/${repo}/releases?per_page=100`;
-  const res = await http.getJson<GithubRelease[]>(url, {
-    paginate: true,
-  });
-  const githubReleases = res.body;
-  const dependency: ReleaseResult = {
-    sourceUrl: getSourceUrl(repo, registryUrl),
-    releases: null,
-  };
-  dependency.releases = githubReleases
-    .filter(({ draft }) => draft !== true)
-    .map(({ tag_name, published_at, prerelease }) => ({
-      version: tag_name,
-      gitRef: tag_name,
-      releaseTimestamp: published_at,
-      isStable: prerelease ? false : undefined,
-    }));
-  const cacheMinutes = 10;
-  await packageCache.set(cacheNamespace, cacheKey, dependency, cacheMinutes);
-  return dependency;
-}
 
-function getDigestCacheKey(
-  { lookupName: repo, currentValue, currentDigest, registryUrl }: DigestConfig,
-  newValue: string
-): string {
-  const type = 'digest';
-  return `${registryUrl}:${repo}:${currentValue}:${currentDigest}:${newValue}:${type}`;
-}
-
-/**
- * github.getDigest
- *
- * The `newValue` supplied here should be a valid tag for the GitHub release.
- * Requires `currentValue` and `currentDigest`.
- *
- * There may be many assets attached to the release. This function will:
- *  - Identify the asset pinned by `currentDigest` in the `currentValue` release
- *     - Download small release assets, parse as checksum manifests (e.g. `SHASUMS.txt`).
- *     - Download individual assets until `currentDigest` is encountered. This is limited to sha256 and sha512.
- *  - Map the hashed asset to `newValue` and return the updated digest as a string
- */
-export async function getDigest(
-  { lookupName: repo, currentValue, currentDigest, registryUrl }: DigestConfig,
-  newValue?: string
-): Promise<string | null> {
-  logger.debug(
-    { repo, currentValue, currentDigest, registryUrl, newValue },
-    'getDigest'
-  );
-  if (!currentDigest) {
+  /** Given a digest asset, find the equivalent digest in a different release. */
+  async mapDigestAssetToRelease(
+    digestAsset: DigestAsset,
+    release: GithubRelease
+  ): Promise<string | null> {
+    const current = digestAsset.currentVersion.replace(regEx(/^v/), '');
+    const next = release.tag_name.replace(regEx(/^v/), '');
+    const releaseChecksumAssetName = digestAsset.assetName.replace(
+      current,
+      next
+    );
+    const releaseAsset = release.assets.find(
+      (a: GithubReleaseAsset) => a.name === releaseChecksumAssetName
+    );
+    if (!releaseAsset) {
+      return null;
+    }
+    if (digestAsset.digestedFileName) {
+      const releaseFilename = digestAsset.digestedFileName.replace(
+        current,
+        next
+      );
+      const res = await this.http.get(releaseAsset.browser_download_url);
+      for (const line of res.body.split(newlineRegex)) {
+        const [lineDigest, lineFn] = line.split(regEx(/\s+/), 2);
+        if (lineFn === releaseFilename) {
+          return lineDigest;
+        }
+      }
+    } else {
+      const algorithm = inferHashAlg(digestAsset.currentDigest);
+      const newDigest = await this.downloadAndDigest(releaseAsset, algorithm);
+      return newDigest;
+    }
     return null;
   }
-  if (!currentValue) {
-    return currentDigest;
-  }
-  const cacheKey = getDigestCacheKey(
-    { lookupName: repo, currentValue, currentDigest, registryUrl },
-    newValue
-  );
-  const cachedResult = await packageCache.get<string>(cacheNamespace, cacheKey);
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
-  }
 
-  const apiBaseUrl = getApiBaseUrl(registryUrl);
-  const currentRelease = await getGithubRelease(apiBaseUrl, repo, currentValue);
-  const digestAsset = await findDigestAsset(currentRelease, currentDigest);
-  let newDigest: string;
-  if (!digestAsset || newValue === currentValue) {
-    newDigest = currentDigest;
-  } else {
-    const newRelease = await getGithubRelease(apiBaseUrl, repo, newValue);
-    newDigest = await mapDigestAssetToRelease(digestAsset, newRelease);
+  @cache({
+    ttlMinutes: 1440,
+    namespace: 'datasource-github-releases',
+    key: (
+      {
+        lookupName: repo,
+        currentValue,
+        currentDigest,
+        registryUrl,
+      }: DigestConfig,
+      newValue?: string
+    ) =>
+      `${registryUrl}:${repo}:${currentValue}:${currentDigest}:${newValue}:digest`,
+  })
+  /**
+   * github.getDigest
+   *
+   * The `newValue` supplied here should be a valid tag for the GitHub release.
+   * Requires `currentValue` and `currentDigest`.
+   *
+   * There may be many assets attached to the release. This function will:
+   *  - Identify the asset pinned by `currentDigest` in the `currentValue` release
+   *     - Download small release assets, parse as checksum manifests (e.g. `SHASUMS.txt`).
+   *     - Download individual assets until `currentDigest` is encountered. This is limited to sha256 and sha512.
+   *  - Map the hashed asset to `newValue` and return the updated digest as a string
+   */
+  override async getDigest(
+    {
+      lookupName: repo,
+      currentValue,
+      currentDigest,
+      registryUrl,
+    }: DigestConfig,
+    newValue: string
+  ): Promise<string | null> {
+    logger.debug(
+      { repo, currentValue, currentDigest, registryUrl, newValue },
+      'getDigest'
+    );
+    if (!currentDigest) {
+      return null;
+    }
+    if (!currentValue) {
+      return currentDigest;
+    }
+
+    const apiBaseUrl = getApiBaseUrl(registryUrl);
+    const { body: currentRelease } = await this.http.getJson<GithubRelease>(
+      `${apiBaseUrl}repos/${repo}/releases/tags/${currentValue}`
+    );
+    const digestAsset = await this.findDigestAsset(
+      currentRelease,
+      currentDigest
+    );
+    let newDigest: string;
+    if (!digestAsset || newValue === currentValue) {
+      newDigest = currentDigest;
+    } else {
+      const { body: newRelease } = await this.http.getJson<GithubRelease>(
+        `${apiBaseUrl}repos/${repo}/releases/tags/${newValue}`
+      );
+      newDigest = await this.mapDigestAssetToRelease(digestAsset, newRelease);
+    }
+    return newDigest;
   }
 
-  const cacheMinutes = 1440;
-  await packageCache.set(cacheNamespace, cacheKey, newDigest, cacheMinutes);
-  return newDigest;
+  @cache({
+    namespace: 'datasource-github-releases',
+    key: ({ lookupName: repo, registryUrl }: GetReleasesConfig) =>
+      `${registryUrl}:${repo}:tags`,
+  })
+  /**
+   * github.getReleases
+   *
+   * This function can be used to fetch releases with a customisable versioning (e.g. semver) and with releases.
+   *
+   * This function will:
+   *  - Fetch all releases
+   *  - Sanitize the versions if desired (e.g. strip out leading 'v')
+   *  - Return a dependency object containing sourceUrl string and releases array
+   */
+  async getReleases({
+    lookupName: repo,
+    registryUrl,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
+    const apiBaseUrl = getApiBaseUrl(registryUrl);
+    const url = `${apiBaseUrl}repos/${repo}/releases?per_page=100`;
+    const res = await this.http.getJson<GithubRelease[]>(url, {
+      paginate: true,
+    });
+    const githubReleases = res.body;
+    const dependency: ReleaseResult = {
+      sourceUrl: getSourceUrl(repo, registryUrl),
+      releases: githubReleases
+        .filter(({ draft }) => draft !== true)
+        .map(({ tag_name, published_at, prerelease }) => ({
+          version: tag_name,
+          gitRef: tag_name,
+          releaseTimestamp: published_at,
+          isStable: prerelease ? false : undefined,
+        })),
+    };
+    return dependency;
+  }
 }
diff --git a/lib/datasource/github-tags/index.spec.ts b/lib/datasource/github-tags/index.spec.ts
index 4b8edc4ab8..0d4d47bf9c 100644
--- a/lib/datasource/github-tags/index.spec.ts
+++ b/lib/datasource/github-tags/index.spec.ts
@@ -1,7 +1,7 @@
 import { getPkgReleases } from '..';
 import * as httpMock from '../../../test/http-mock';
 import * as _hostRules from '../../util/host-rules';
-import * as github from '.';
+import { GithubTagsDatasource } from '.';
 
 jest.mock('../../util/host-rules');
 const hostRules: any = _hostRules;
@@ -10,6 +10,8 @@ const githubApiHost = 'https://api.github.com';
 const githubEnterpriseApiHost = 'https://git.enterprise.com';
 
 describe('datasource/github-tags/index', () => {
+  const github = new GithubTagsDatasource();
+
   beforeEach(() => {
     jest.resetAllMocks();
     hostRules.hosts = jest.fn(() => []);
diff --git a/lib/datasource/github-tags/index.ts b/lib/datasource/github-tags/index.ts
index 26719d6096..e3a57eb52a 100644
--- a/lib/datasource/github-tags/index.ts
+++ b/lib/datasource/github-tags/index.ts
@@ -1,185 +1,147 @@
 import { logger } from '../../logger';
-import * as packageCache from '../../util/cache/package';
-import { GithubHttp } from '../../util/http/github';
-import {
-  getApiBaseUrl,
-  getSourceUrl,
-  getReleases as githubGetReleases,
-} from '../github-releases';
+import { cache } from '../../util/cache/package/decorator';
+import { GithubReleasesDatasource } from '../github-releases';
+import { getApiBaseUrl, getSourceUrl } from '../github-releases/common';
 import type { DigestConfig, GetReleasesConfig, ReleaseResult } from '../types';
 import type { GitHubTag, TagResponse } from './types';
 
-export const id = 'github-tags';
-export const customRegistrySupport = true;
-export const defaultRegistryUrls = ['https://github.com'];
-export const registryStrategy = 'first';
+export class GithubTagsDatasource extends GithubReleasesDatasource {
+  static override readonly id = 'github-tags';
 
-const http = new GithubHttp(id);
-
-const cacheNamespace = 'datasource-github-tags';
-
-function getCacheKey(registryUrl: string, repo: string, type: string): string {
-  return `${registryUrl}:${repo}:${type}`;
-}
-
-async function getTagCommit(
-  registryUrl: string,
-  githubRepo: string,
-  tag: string
-): Promise<string | null> {
-  const cachedResult = await packageCache.get<string>(
-    cacheNamespace,
-    getCacheKey(registryUrl, githubRepo, `tag-${tag}`)
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
+  constructor() {
+    super(GithubTagsDatasource.id);
   }
 
-  const apiBaseUrl = getApiBaseUrl(registryUrl);
-  let digest: string;
-  try {
-    const url = `${apiBaseUrl}repos/${githubRepo}/git/refs/tags/${tag}`;
-    const res = (await http.getJson<TagResponse>(url)).body.object;
-    if (res.type === 'commit') {
-      digest = res.sha;
-    } else if (res.type === 'tag') {
-      digest = (await http.getJson<TagResponse>(res.url)).body.object.sha;
-    } else {
-      logger.warn({ res }, 'Unknown git tag refs type');
+  @cache({
+    ttlMinutes: 120,
+    namespace: `datasource-${GithubTagsDatasource.id}`,
+    key: (registryUrl: string, githubRepo: string, tag: string) =>
+      `${registryUrl}:${githubRepo}:tag-${tag}`,
+  })
+  async getTagCommit(
+    registryUrl: string,
+    githubRepo: string,
+    tag: string
+  ): Promise<string | null> {
+    const apiBaseUrl = getApiBaseUrl(registryUrl);
+    let digest: string | null = null;
+    try {
+      const url = `${apiBaseUrl}repos/${githubRepo}/git/refs/tags/${tag}`;
+      const res = (await this.http.getJson<TagResponse>(url)).body.object;
+      if (res.type === 'commit') {
+        digest = res.sha;
+      } else if (res.type === 'tag') {
+        digest = (await this.http.getJson<TagResponse>(res.url)).body.object
+          .sha;
+      } else {
+        logger.warn({ res }, 'Unknown git tag refs type');
+      }
+    } catch (err) {
+      logger.debug(
+        { githubRepo, err },
+        'Error getting tag commit from GitHub repo'
+      );
     }
-  } catch (err) {
-    logger.debug(
-      { githubRepo, err },
-      'Error getting tag commit from GitHub repo'
-    );
+    return digest;
   }
-  if (!digest) {
-    return null;
-  }
-  const cacheMinutes = 120;
-  await packageCache.set(
-    cacheNamespace,
-    getCacheKey(registryUrl, githubRepo, `tag-${tag}`),
-    digest,
-    cacheMinutes
-  );
-  return digest;
-}
 
-/**
- * github.getDigest
- *
- * The `newValue` supplied here should be a valid tag for the docker image.
- *
- * This function will simply return the latest commit hash for the configured repository.
- */
-export async function getDigest(
-  { lookupName: repo, registryUrl }: Partial<DigestConfig>,
-  newValue?: string
-): Promise<string | null> {
-  if (newValue?.length) {
-    return getTagCommit(registryUrl, repo, newValue);
-  }
-  const cachedResult = await packageCache.get<string>(
-    cacheNamespace,
-    getCacheKey(registryUrl, repo, 'commit')
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
-  }
-  const apiBaseUrl = getApiBaseUrl(registryUrl);
-  let digest: string;
-  try {
-    const url = `${apiBaseUrl}repos/${repo}/commits?per_page=1`;
-    const res = await http.getJson<{ sha: string }[]>(url);
-    digest = res.body[0].sha;
-  } catch (err) {
-    logger.debug(
-      { githubRepo: repo, err, registryUrl },
-      'Error getting latest commit from GitHub repo'
-    );
-  }
-  if (!digest) {
-    return null;
+  @cache({
+    ttlMinutes: 10,
+    namespace: `datasource-${GithubTagsDatasource.id}`,
+    key: (registryUrl: string, githubRepo: string) =>
+      `${registryUrl}:${githubRepo}:commit`,
+  })
+  async getCommit(
+    registryUrl: string,
+    githubRepo: string
+  ): Promise<string | null> {
+    const apiBaseUrl = getApiBaseUrl(registryUrl);
+    let digest: string | null = null;
+    try {
+      const url = `${apiBaseUrl}repos/${githubRepo}/commits?per_page=1`;
+      const res = await this.http.getJson<{ sha: string }[]>(url);
+      digest = res.body[0].sha;
+    } catch (err) {
+      logger.debug(
+        { githubRepo: githubRepo, err, registryUrl },
+        'Error getting latest commit from GitHub repo'
+      );
+    }
+    return digest;
   }
-  const cacheMinutes = 10;
-  await packageCache.set(
-    cacheNamespace,
-    getCacheKey(registryUrl, repo, 'commit'),
-    digest,
-    cacheMinutes
-  );
-  return digest;
-}
 
-async function getTags({
-  registryUrl,
-  lookupName: repo,
-}: GetReleasesConfig): Promise<ReleaseResult | null> {
-  const cachedResult = await packageCache.get<ReleaseResult>(
-    cacheNamespace,
-    getCacheKey(registryUrl, repo, 'tags')
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
+  /**
+   * github.getDigest
+   *
+   * The `newValue` supplied here should be a valid tag for the docker image.
+   *
+   * Returns the latest commit hash for the repository.
+   */
+  override getDigest(
+    { lookupName: repo, registryUrl }: Partial<DigestConfig>,
+    newValue?: string
+  ): Promise<string | null> {
+    return newValue
+      ? this.getTagCommit(registryUrl, repo, newValue)
+      : this.getCommit(registryUrl, repo);
   }
 
-  const apiBaseUrl = getApiBaseUrl(registryUrl);
-  // tag
-  const url = `${apiBaseUrl}repos/${repo}/tags?per_page=100`;
+  @cache({
+    ttlMinutes: 10,
+    namespace: `datasource-${GithubTagsDatasource.id}`,
+    key: ({ registryUrl, lookupName: repo }: GetReleasesConfig) =>
+      `${registryUrl}:${repo}:tags`,
+  })
+  async getTags({
+    registryUrl,
+    lookupName: repo,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
+    const apiBaseUrl = getApiBaseUrl(registryUrl);
+    // tag
+    const url = `${apiBaseUrl}repos/${repo}/tags?per_page=100`;
 
-  const versions = (
-    await http.getJson<GitHubTag[]>(url, {
-      paginate: true,
-    })
-  ).body.map((o) => o.name);
-  const dependency: ReleaseResult = {
-    sourceUrl: getSourceUrl(repo, registryUrl),
-    releases: null,
-  };
-  dependency.releases = versions.map((version) => ({
-    version,
-    gitRef: version,
-  }));
-  const cacheMinutes = 10;
-  await packageCache.set(
-    cacheNamespace,
-    getCacheKey(registryUrl, repo, 'tags'),
-    dependency,
-    cacheMinutes
-  );
-  return dependency;
-}
+    const versions = (
+      await this.http.getJson<GitHubTag[]>(url, {
+        paginate: true,
+      })
+    ).body.map((o) => o.name);
+    const dependency: ReleaseResult = {
+      sourceUrl: getSourceUrl(repo, registryUrl),
+      releases: versions.map((version) => ({
+        version,
+        gitRef: version,
+      })),
+    };
+    return dependency;
+  }
 
-export async function getReleases(
-  config: GetReleasesConfig
-): Promise<ReleaseResult | null> {
-  const tagsResult = await getTags(config);
+  override async getReleases(
+    config: GetReleasesConfig
+  ): Promise<ReleaseResult | null> {
+    const tagsResult = await this.getTags(config);
 
-  try {
-    // Fetch additional data from releases endpoint when possible
-    const releasesResult = await githubGetReleases(config);
-    const releaseByVersion = {};
-    releasesResult?.releases?.forEach((release) => {
-      const key = release.version;
-      const value = { ...release };
-      delete value.version;
-      releaseByVersion[key] = value;
-    });
+    try {
+      // Fetch additional data from releases endpoint when possible
+      const releasesResult = await super.getReleases(config);
+      const releaseByVersion = {};
+      releasesResult?.releases?.forEach((release) => {
+        const key = release.version;
+        const value = { ...release };
+        delete value.version;
+        releaseByVersion[key] = value;
+      });
 
-    const mergedReleases = [];
-    tagsResult.releases.forEach((tag) => {
-      const release = releaseByVersion[tag.version];
-      mergedReleases.push({ ...release, ...tag });
-    });
+      const mergedReleases = [];
+      tagsResult.releases.forEach((tag) => {
+        const release = releaseByVersion[tag.version];
+        mergedReleases.push({ ...release, ...tag });
+      });
 
-    tagsResult.releases = mergedReleases;
-  } catch (e) {
-    // no-op
-  }
+      tagsResult.releases = mergedReleases;
+    } catch (e) {
+      // no-op
+    }
 
-  return tagsResult;
+    return tagsResult;
+  }
 }
diff --git a/lib/datasource/go/__snapshots__/index.spec.ts.snap b/lib/datasource/go/__snapshots__/index.spec.ts.snap
index 88edf44457..e0552d5ce0 100644
--- a/lib/datasource/go/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/go/__snapshots__/index.spec.ts.snap
@@ -11,16 +11,6 @@ Array [
     "method": "GET",
     "url": "https://golang.org/x/text?go-get=1",
   },
-  Object {
-    "headers": Object {
-      "accept": "application/vnd.github.v3+json",
-      "accept-encoding": "gzip, deflate, br",
-      "host": "api.github.com",
-      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
-    },
-    "method": "GET",
-    "url": "https://api.github.com/repos/golang/text/commits?per_page=1",
-  },
 ]
 `;
 
diff --git a/lib/datasource/go/base.spec.ts b/lib/datasource/go/base.spec.ts
index cfec055597..47184e326d 100644
--- a/lib/datasource/go/base.spec.ts
+++ b/lib/datasource/go/base.spec.ts
@@ -1,7 +1,7 @@
 import * as httpMock from '../../../test/http-mock';
 import { loadFixture, mocked } from '../../../test/util';
 import * as _hostRules from '../../util/host-rules';
-import { id as githubDatasource } from '../github-tags';
+import { GithubTagsDatasource } from '../github-tags';
 import { GitlabTagsDatasource } from '../gitlab-tags';
 import { BaseGoDatasource } from './base';
 
@@ -90,7 +90,7 @@ describe('datasource/go/base', () => {
         const res = await BaseGoDatasource.getDatasource('golang.org/x/text');
 
         expect(res).toEqual({
-          datasource: githubDatasource,
+          datasource: GithubTagsDatasource.id,
           lookupName: 'golang/text',
           registryUrl: 'https://github.com',
         });
@@ -107,7 +107,7 @@ describe('datasource/go/base', () => {
         );
 
         expect(res).toEqual({
-          datasource: githubDatasource,
+          datasource: GithubTagsDatasource.id,
           lookupName: 'example/module',
           registryUrl: 'https://git.enterprise.com',
         });
diff --git a/lib/datasource/go/base.ts b/lib/datasource/go/base.ts
index 1dfc69c12f..b831b8eebf 100644
--- a/lib/datasource/go/base.ts
+++ b/lib/datasource/go/base.ts
@@ -6,7 +6,7 @@ import { Http } from '../../util/http';
 import { regEx } from '../../util/regex';
 import { trimTrailingSlash } from '../../util/url';
 import { BitBucketTagsDatasource } from '../bitbucket-tags';
-import * as github from '../github-tags';
+import { GithubTagsDatasource } from '../github-tags';
 import { GitlabTagsDatasource } from '../gitlab-tags';
 import type { DataSource } from './types';
 
@@ -27,7 +27,7 @@ export class BaseGoDatasource {
       const [pkg] = goModule.replace('gopkg.in/', '').split('.');
       const lookupName = pkg.includes('/') ? pkg : `go-${pkg}/${pkg}`;
       return {
-        datasource: github.id,
+        datasource: GithubTagsDatasource.id,
         lookupName,
         registryUrl: 'https://github.com',
       };
@@ -37,7 +37,7 @@ export class BaseGoDatasource {
       const split = goModule.split('/');
       const lookupName = split[1] + '/' + split[2];
       return {
-        datasource: github.id,
+        datasource: GithubTagsDatasource.id,
         lookupName,
         registryUrl: 'https://github.com',
       };
@@ -74,7 +74,7 @@ export class BaseGoDatasource {
       logger.debug({ goModule, goSourceUrl }, 'Go lookup source url');
       if (goSourceUrl?.startsWith('https://github.com/')) {
         return {
-          datasource: github.id,
+          datasource: GithubTagsDatasource.id,
           lookupName: goSourceUrl
             .replace('https://github.com/', '')
             .replace(regEx(/\/$/), ''),
@@ -159,7 +159,7 @@ export class BaseGoDatasource {
           .join('/');
 
         return {
-          datasource: github.id,
+          datasource: GithubTagsDatasource.id,
           registryUrl: `${parsedUrl.protocol}//${parsedUrl.host}`,
           lookupName,
         };
diff --git a/lib/datasource/go/common.ts b/lib/datasource/go/common.ts
index 335d4ef952..4b928d29fd 100644
--- a/lib/datasource/go/common.ts
+++ b/lib/datasource/go/common.ts
@@ -1,6 +1,6 @@
 import { BitBucketTagsDatasource } from '../bitbucket-tags';
-import { getSourceUrl as githubSourceUrl } from '../github-releases';
-import { id as githubDatasource } from '../github-tags';
+import { getSourceUrl as githubSourceUrl } from '../github-releases/common';
+import { GithubTagsDatasource } from '../github-tags';
 import { GitlabTagsDatasource } from '../gitlab-tags';
 import { getSourceUrl as gitlabSourceUrl } from '../gitlab-tags/util';
 
@@ -16,7 +16,7 @@ export function getSourceUrl(dataSource?: DataSource): string | undefined {
   if (dataSource) {
     const { datasource, registryUrl, lookupName } = dataSource;
 
-    if (datasource === githubDatasource) {
+    if (datasource === GithubTagsDatasource.id) {
       return githubSourceUrl(lookupName, registryUrl);
     }
 
diff --git a/lib/datasource/go/index.spec.ts b/lib/datasource/go/index.spec.ts
index b9020f5b08..25dc3f3b1f 100644
--- a/lib/datasource/go/index.spec.ts
+++ b/lib/datasource/go/index.spec.ts
@@ -7,19 +7,20 @@ jest.mock('../../util/host-rules');
 const hostRules = mocked(_hostRules);
 
 const getReleasesDirectMock = jest.fn();
+
+const getDigestGithubMock = jest.fn();
 const getDigestGitlabMock = jest.fn();
 const getDigestBitbucketMock = jest.fn();
 jest.mock('./releases-direct', () => {
   return {
-    GoDirectDatasource: jest.fn().mockImplementation(() => ({
-      gitlab: {
-        getDigest: () => getDigestGitlabMock(),
-      },
-      bitbucket: {
-        getDigest: () => getDigestBitbucketMock(),
-      },
-      getReleases: () => getReleasesDirectMock(),
-    })),
+    GoDirectDatasource: jest.fn().mockImplementation(() => {
+      return {
+        github: { getDigest: () => getDigestGithubMock() },
+        gitlab: { getDigest: () => getDigestGitlabMock() },
+        bitbucket: { getDigest: () => getDigestBitbucketMock() },
+        getReleases: () => getReleasesDirectMock(),
+      };
+    }),
   };
 });
 
@@ -144,10 +145,7 @@ describe('datasource/go/index', () => {
         .scope('https://golang.org/')
         .get('/x/text?go-get=1')
         .reply(200, loadFixture('go-get-github.html'));
-      httpMock
-        .scope('https://api.github.com/')
-        .get('/repos/golang/text/commits?per_page=1')
-        .reply(200, [{ sha: 'abcdefabcdefabcdefabcdef' }]);
+      getDigestGithubMock.mockResolvedValueOnce('abcdefabcdefabcdefabcdef');
       const res = await datasource.getDigest(
         { lookupName: 'golang.org/x/text' },
         null
diff --git a/lib/datasource/go/index.ts b/lib/datasource/go/index.ts
index 7ae6da4560..6218d0ff2d 100644
--- a/lib/datasource/go/index.ts
+++ b/lib/datasource/go/index.ts
@@ -1,7 +1,7 @@
 import { cache } from '../../util/cache/package/decorator';
 import { BitBucketTagsDatasource } from '../bitbucket-tags';
 import { Datasource } from '../datasource';
-import * as github from '../github-tags';
+import { GithubTagsDatasource } from '../github-tags';
 import { GitlabTagsDatasource } from '../gitlab-tags';
 import type { DigestConfig, GetReleasesConfig, ReleaseResult } from '../types';
 import { BaseGoDatasource } from './base';
@@ -57,8 +57,8 @@ export class GoDatasource extends Datasource {
     const tag = value && !value.startsWith('v0.0.0-2') ? value : undefined;
 
     switch (source.datasource) {
-      case github.id: {
-        return github.getDigest(source, tag);
+      case GithubTagsDatasource.id: {
+        return this.direct.github.getDigest(source, tag);
       }
       case BitBucketTagsDatasource.id: {
         return this.direct.bitbucket.getDigest(source, tag);
diff --git a/lib/datasource/go/releases-direct.ts b/lib/datasource/go/releases-direct.ts
index 71e9089bcf..858d169f7c 100644
--- a/lib/datasource/go/releases-direct.ts
+++ b/lib/datasource/go/releases-direct.ts
@@ -3,7 +3,7 @@ import { cache } from '../../util/cache/package/decorator';
 import { regEx } from '../../util/regex';
 import { BitBucketTagsDatasource } from '../bitbucket-tags';
 import { Datasource } from '../datasource';
-import * as github from '../github-tags';
+import { GithubTagsDatasource } from '../github-tags';
 import { GitlabTagsDatasource } from '../gitlab-tags';
 import type { DatasourceApi, GetReleasesConfig, ReleaseResult } from '../types';
 import { BaseGoDatasource } from './base';
@@ -12,11 +12,13 @@ import { getSourceUrl } from './common';
 export class GoDirectDatasource extends Datasource {
   static readonly id = 'go-direct';
 
+  github: GithubTagsDatasource;
   gitlab: DatasourceApi;
   bitbucket: DatasourceApi;
 
   constructor() {
     super(GoDirectDatasource.id);
+    this.github = new GithubTagsDatasource();
     this.gitlab = new GitlabTagsDatasource();
     this.bitbucket = new BitBucketTagsDatasource();
   }
@@ -53,8 +55,8 @@ export class GoDirectDatasource extends Datasource {
     }
 
     switch (source.datasource) {
-      case github.id: {
-        res = await github.getReleases(source);
+      case GithubTagsDatasource.id: {
+        res = await this.github.getReleases(source);
         break;
       }
       case GitlabTagsDatasource.id: {
diff --git a/lib/manager/ansible-galaxy/collections.ts b/lib/manager/ansible-galaxy/collections.ts
index 2f5824d557..a062fc1a7c 100644
--- a/lib/manager/ansible-galaxy/collections.ts
+++ b/lib/manager/ansible-galaxy/collections.ts
@@ -1,6 +1,6 @@
 import { GalaxyCollectionDatasource } from '../../datasource/galaxy-collection';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { regEx } from '../../util/regex';
 import type { PackageDependency } from '../types';
 import {
@@ -53,7 +53,7 @@ function handleGitDep(
   if (nameMatch) {
     // if a github.com repository is referenced use github-tags instead of git-tags
     if (nameMatch.groups.hostname === 'github.com') {
-      dep.datasource = datasourceGithubTags.id;
+      dep.datasource = GithubTagsDatasource.id;
     } else {
       dep.datasource = GitTagsDatasource.id;
     }
diff --git a/lib/manager/ansible-galaxy/index.ts b/lib/manager/ansible-galaxy/index.ts
index fc92ebe669..426bef1ad2 100644
--- a/lib/manager/ansible-galaxy/index.ts
+++ b/lib/manager/ansible-galaxy/index.ts
@@ -1,6 +1,6 @@
 import { GalaxyCollectionDatasource } from '../../datasource/galaxy-collection';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 
 export { extractPackageFile } from './extract';
 
@@ -11,5 +11,5 @@ export const defaultConfig = {
 export const supportedDatasources = [
   GalaxyCollectionDatasource.id,
   GitTagsDatasource.id,
-  datasourceGithubTags.id,
+  GithubTagsDatasource.id,
 ];
diff --git a/lib/manager/batect-wrapper/extract.spec.ts b/lib/manager/batect-wrapper/extract.spec.ts
index e2fcdd4298..00053254a7 100644
--- a/lib/manager/batect-wrapper/extract.spec.ts
+++ b/lib/manager/batect-wrapper/extract.spec.ts
@@ -1,5 +1,5 @@
 import { Fixtures } from '../../../test/fixtures';
-import { id as githubReleaseDatasource } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { id as semverVersioning } from '../../versioning/semver';
 import type { PackageDependency } from '../types';
 import { extractPackageFile } from './extract';
@@ -21,7 +21,7 @@ describe('manager/batect-wrapper/extract', () => {
         depName: 'batect/batect',
         commitMessageTopic: 'Batect',
         currentValue: '0.60.1',
-        datasource: githubReleaseDatasource,
+        datasource: GithubReleasesDatasource.id,
         versioning: semverVersioning,
       };
 
@@ -35,7 +35,7 @@ describe('manager/batect-wrapper/extract', () => {
         depName: 'batect/batect',
         commitMessageTopic: 'Batect',
         currentValue: '0.60.1',
-        datasource: githubReleaseDatasource,
+        datasource: GithubReleasesDatasource.id,
         versioning: semverVersioning,
       };
 
diff --git a/lib/manager/batect-wrapper/extract.ts b/lib/manager/batect-wrapper/extract.ts
index 3738893207..64be79eb10 100644
--- a/lib/manager/batect-wrapper/extract.ts
+++ b/lib/manager/batect-wrapper/extract.ts
@@ -1,4 +1,4 @@
-import { id as githubReleaseDatasource } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
 import { id as semverVersioning } from '../../versioning/semver';
@@ -18,7 +18,7 @@ export function extractPackageFile(fileContent: string): PackageFile | null {
     depName: 'batect/batect',
     commitMessageTopic: 'Batect',
     currentValue: match[1],
-    datasource: githubReleaseDatasource,
+    datasource: GithubReleasesDatasource.id,
     versioning: semverVersioning,
   };
 
diff --git a/lib/manager/batect-wrapper/index.ts b/lib/manager/batect-wrapper/index.ts
index dcaa1c1e63..80732b481d 100644
--- a/lib/manager/batect-wrapper/index.ts
+++ b/lib/manager/batect-wrapper/index.ts
@@ -1,4 +1,4 @@
-import { id as githubReleaseDatasource } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { id as versioning } from '../../versioning/semver';
 
 export { extractPackageFile } from './extract';
@@ -9,4 +9,4 @@ export const defaultConfig = {
   versioning,
 };
 
-export const supportedDatasources = [githubReleaseDatasource];
+export const supportedDatasources = [GithubReleasesDatasource.id];
diff --git a/lib/manager/bazel/extract.ts b/lib/manager/bazel/extract.ts
index a35ac770be..58a8797981 100644
--- a/lib/manager/bazel/extract.ts
+++ b/lib/manager/bazel/extract.ts
@@ -3,8 +3,8 @@ import { parse as _parse } from 'url';
 import parse from 'github-url-from-git';
 import moo from 'moo';
 import * as datasourceDocker from '../../datasource/docker';
-import * as datasourceGithubReleases from '../../datasource/github-releases';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GoDatasource } from '../../datasource/go';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
@@ -26,11 +26,11 @@ function parseUrl(urlString: string): UrlParsedResult | null {
   let datasource: string;
   let currentValue: string = null;
   if (path[2] === 'releases' && path[3] === 'download') {
-    datasource = datasourceGithubReleases.id;
+    datasource = GithubReleasesDatasource.id;
     currentValue = path[4];
   }
   if (path[2] === 'archive') {
-    datasource = datasourceGithubTags.id;
+    datasource = GithubTagsDatasource.id;
     currentValue = path[3];
     // Strip archive extension to get hash or tag.
     // Tolerates formats produced by Git(Hub|Lab) and allowed by http_archive
@@ -241,7 +241,7 @@ export function extractPackageFile(
       const githubURL = parse(remote);
       if (githubURL) {
         const repo = githubURL.substring('https://github.com/'.length);
-        dep.datasource = datasourceGithubReleases.id;
+        dep.datasource = GithubReleasesDatasource.id;
         dep.lookupName = repo;
         deps.push(dep);
       }
diff --git a/lib/manager/bazel/index.ts b/lib/manager/bazel/index.ts
index aa0568fcec..bf8cf264a1 100644
--- a/lib/manager/bazel/index.ts
+++ b/lib/manager/bazel/index.ts
@@ -1,6 +1,6 @@
 import * as datasourceDocker from '../../datasource/docker';
-import * as datasourceGithubReleases from '../../datasource/github-releases';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GoDatasource } from '../../datasource/go';
 import { extractPackageFile } from './extract';
 import { updateDependency } from './update';
@@ -13,7 +13,7 @@ export const defaultConfig = {
 
 export const supportedDatasources = [
   datasourceDocker.id,
-  datasourceGithubReleases.id,
-  datasourceGithubTags.id,
+  GithubReleasesDatasource.id,
+  GithubTagsDatasource.id,
   GoDatasource.id,
 ];
diff --git a/lib/manager/buildkite/extract.ts b/lib/manager/buildkite/extract.ts
index 802652f221..75359bd278 100644
--- a/lib/manager/buildkite/extract.ts
+++ b/lib/manager/buildkite/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { logger } from '../../logger';
 import type { SkipReason } from '../../types';
 import { newlineRegex, regEx } from '../../util/regex';
@@ -47,7 +47,7 @@ export function extractPackageFile(content: string): PackageFile | null {
               depName: gitPluginName,
               currentValue: currentValue,
               registryUrls: ['https://' + registry],
-              datasource: datasourceGithubTags.id,
+              datasource: GithubTagsDatasource.id,
             };
             deps.push(dep);
             continue;
@@ -77,7 +77,7 @@ export function extractPackageFile(content: string): PackageFile | null {
             skipReason,
           };
           if (repo) {
-            dep.datasource = datasourceGithubTags.id;
+            dep.datasource = GithubTagsDatasource.id;
             dep.lookupName = repo;
           }
           deps.push(dep);
diff --git a/lib/manager/buildkite/index.ts b/lib/manager/buildkite/index.ts
index 3350b60ce6..028d54c729 100644
--- a/lib/manager/buildkite/index.ts
+++ b/lib/manager/buildkite/index.ts
@@ -1,4 +1,4 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { extractPackageFile } from './extract';
 
 export { extractPackageFile };
@@ -10,4 +10,4 @@ export const defaultConfig = {
     'to {{#if isMajor}}v{{{newMajor}}}{{else}}{{{newValue}}}{{/if}}',
 };
 
-export const supportedDatasources = [datasourceGithubTags.id];
+export const supportedDatasources = [GithubTagsDatasource.id];
diff --git a/lib/manager/cocoapods/extract.ts b/lib/manager/cocoapods/extract.ts
index 84781e1930..176f536188 100644
--- a/lib/manager/cocoapods/extract.ts
+++ b/lib/manager/cocoapods/extract.ts
@@ -1,5 +1,5 @@
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
 import { PodDatasource } from '../../datasource/pod';
 import { logger } from '../../logger';
@@ -61,7 +61,7 @@ export function gitDep(parsedLine: ParsedLine): PackageDependency | null {
     if (account && repo) {
       const datasource =
         platform === 'github'
-          ? datasourceGithubTags.id
+          ? GithubTagsDatasource.id
           : GitlabTagsDatasource.id;
       return {
         datasource,
diff --git a/lib/manager/cocoapods/index.ts b/lib/manager/cocoapods/index.ts
index 880639894b..a2230e9dbf 100644
--- a/lib/manager/cocoapods/index.ts
+++ b/lib/manager/cocoapods/index.ts
@@ -1,5 +1,5 @@
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
 import { PodDatasource } from '../../datasource/pod';
 import * as rubyVersioning from '../../versioning/ruby';
@@ -14,7 +14,7 @@ export const defaultConfig = {
 
 export const supportedDatasources = [
   GitTagsDatasource.id,
-  datasourceGithubTags.id,
+  GithubTagsDatasource.id,
   GitlabTagsDatasource.id,
   PodDatasource.id,
 ];
diff --git a/lib/manager/flux/extract.ts b/lib/manager/flux/extract.ts
index b60a35dc21..7c1cb51b53 100644
--- a/lib/manager/flux/extract.ts
+++ b/lib/manager/flux/extract.ts
@@ -1,5 +1,5 @@
 import { loadAll } from 'js-yaml';
-import { id as GithubReleasesId } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { HelmDatasource } from '../../datasource/helm';
 import { logger } from '../../logger';
 import { readLocalFile } from '../../util/fs';
@@ -77,7 +77,7 @@ function resolveManifest(
       return [
         {
           depName: 'fluxcd/flux2',
-          datasource: GithubReleasesId,
+          datasource: GithubReleasesDatasource.id,
           currentValue: manifest.version,
         },
       ];
diff --git a/lib/manager/flux/index.ts b/lib/manager/flux/index.ts
index f750f47cbe..2bb039bd59 100644
--- a/lib/manager/flux/index.ts
+++ b/lib/manager/flux/index.ts
@@ -1,4 +1,4 @@
-import { id as GithubReleasesId } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { HelmDatasource } from '../../datasource/helm';
 import { systemManifestRegex } from './common';
 
@@ -9,4 +9,7 @@ export const defaultConfig = {
   fileMatch: [systemManifestRegex],
 };
 
-export const supportedDatasources = [GithubReleasesId, HelmDatasource.id];
+export const supportedDatasources = [
+  GithubReleasesDatasource.id,
+  HelmDatasource.id,
+];
diff --git a/lib/manager/github-actions/extract.ts b/lib/manager/github-actions/extract.ts
index d29f9b2426..c322c1cb5d 100644
--- a/lib/manager/github-actions/extract.ts
+++ b/lib/manager/github-actions/extract.ts
@@ -1,4 +1,4 @@
-import * as githubTagsDatasource from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { logger } from '../../logger';
 import { newlineRegex, regEx } from '../../util/regex';
 import * as dockerVersioning from '../../versioning/docker';
@@ -43,7 +43,7 @@ export function extractPackageFile(content: string): PackageFile | null {
       const dep: PackageDependency = {
         depName,
         commitMessageTopic: '{{{depName}}} action',
-        datasource: githubTagsDatasource.id,
+        datasource: GithubTagsDatasource.id,
         versioning: dockerVersioning.id,
         depType: 'action',
         replaceString,
diff --git a/lib/manager/github-actions/index.ts b/lib/manager/github-actions/index.ts
index 5d39f85406..eee8b346fb 100644
--- a/lib/manager/github-actions/index.ts
+++ b/lib/manager/github-actions/index.ts
@@ -1,4 +1,4 @@
-import * as githubTagsDatasource from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import * as dockerVersioning from '../../versioning/docker';
 
 export { extractPackageFile } from './extract';
@@ -11,6 +11,6 @@ export const defaultConfig = {
 };
 
 export const supportedDatasources = [
-  githubTagsDatasource.id,
+  GithubTagsDatasource.id,
   dockerVersioning.id,
 ];
diff --git a/lib/manager/homebrew/extract.ts b/lib/manager/homebrew/extract.ts
index 8f15d69bd1..1de36ed73f 100644
--- a/lib/manager/homebrew/extract.ts
+++ b/lib/manager/homebrew/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { logger } from '../../logger';
 import type { SkipReason } from '../../types';
 import { regEx } from '../../util/regex';
@@ -167,7 +167,7 @@ export function extractPackageFile(content: string): PackageFile | null {
     depName: `${ownerName}/${repoName}`,
     managerData: { ownerName, repoName, sha256, url },
     currentValue,
-    datasource: datasourceGithubTags.id,
+    datasource: GithubTagsDatasource.id,
   };
   if (skipReason) {
     dep.skipReason = skipReason;
diff --git a/lib/manager/homebrew/index.ts b/lib/manager/homebrew/index.ts
index f41a1a8406..1a6a2a46a4 100644
--- a/lib/manager/homebrew/index.ts
+++ b/lib/manager/homebrew/index.ts
@@ -1,4 +1,4 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 export { extractPackageFile } from './extract';
 export { updateDependency } from './update';
 
@@ -7,4 +7,4 @@ export const defaultConfig = {
   fileMatch: ['^Formula/[^/]+[.]rb$'],
 };
 
-export const supportedDatasources = [datasourceGithubTags.id];
+export const supportedDatasources = [GithubTagsDatasource.id];
diff --git a/lib/manager/kustomize/extract.spec.ts b/lib/manager/kustomize/extract.spec.ts
index a0120ad4f7..c7c2fdfb98 100644
--- a/lib/manager/kustomize/extract.spec.ts
+++ b/lib/manager/kustomize/extract.spec.ts
@@ -1,7 +1,7 @@
 import { loadFixture } from '../../../test/util';
 import * as datasourceDocker from '../../datasource/docker';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGitHubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { HelmDatasource } from '../../datasource/helm';
 import {
   extractHelmChart,
@@ -44,7 +44,7 @@ describe('manager/kustomize/extract', () => {
       const version = 'v1.0.0';
       const sample = {
         currentValue: version,
-        datasource: datasourceGitHubTags.id,
+        datasource: GithubTagsDatasource.id,
         depName: 'user/test-repo',
       };
 
@@ -89,7 +89,7 @@ describe('manager/kustomize/extract', () => {
       const version = 'v1.0.0';
       const sample = {
         currentValue: version,
-        datasource: datasourceGitHubTags.id,
+        datasource: GithubTagsDatasource.id,
         depName: 'fluxcd/flux',
       };
 
@@ -101,7 +101,7 @@ describe('manager/kustomize/extract', () => {
       const version = 'v1.0.0';
       const sample = {
         currentValue: version,
-        datasource: datasourceGitHubTags.id,
+        datasource: GithubTagsDatasource.id,
         depName: 'user/repo',
       };
 
@@ -113,7 +113,7 @@ describe('manager/kustomize/extract', () => {
       const version = 'v1.0.0';
       const sample = {
         currentValue: version,
-        datasource: datasourceGitHubTags.id,
+        datasource: GithubTagsDatasource.id,
         depName: 'user/repo',
       };
 
diff --git a/lib/manager/kustomize/extract.ts b/lib/manager/kustomize/extract.ts
index 3b58a1bf6b..7ce2058fd2 100644
--- a/lib/manager/kustomize/extract.ts
+++ b/lib/manager/kustomize/extract.ts
@@ -2,7 +2,7 @@ import is from '@sindresorhus/is';
 import { load } from 'js-yaml';
 import * as datasourceDocker from '../../datasource/docker';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGitHubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { HelmDatasource } from '../../datasource/helm';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
@@ -27,7 +27,7 @@ export function extractResource(base: string): PackageDependency | null {
   if (path.startsWith('github.com:') || path.startsWith('github.com/')) {
     return {
       currentValue: match.groups.currentValue,
-      datasource: datasourceGitHubTags.id,
+      datasource: GithubTagsDatasource.id,
       depName: match.groups.project.replace('.git', ''),
     };
   }
diff --git a/lib/manager/kustomize/index.ts b/lib/manager/kustomize/index.ts
index c3ac88cd6e..0a2d698eb5 100644
--- a/lib/manager/kustomize/index.ts
+++ b/lib/manager/kustomize/index.ts
@@ -1,6 +1,6 @@
 import * as datasourceDocker from '../../datasource/docker';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGitHubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { HelmDatasource } from '../../datasource/helm';
 export { extractPackageFile } from './extract';
 
@@ -12,6 +12,6 @@ export const defaultConfig = {
 export const supportedDatasources = [
   datasourceDocker.id,
   GitTagsDatasource.id,
-  datasourceGitHubTags.id,
+  GithubTagsDatasource.id,
   HelmDatasource.id,
 ];
diff --git a/lib/manager/nodenv/extract.ts b/lib/manager/nodenv/extract.ts
index b8b777cd14..ca3a3941cd 100644
--- a/lib/manager/nodenv/extract.ts
+++ b/lib/manager/nodenv/extract.ts
@@ -1,11 +1,11 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import type { PackageDependency, PackageFile } from '../types';
 
 export function extractPackageFile(content: string): PackageFile {
   const dep: PackageDependency = {
     depName: 'node',
     currentValue: content.trim(),
-    datasource: datasourceGithubTags.id,
+    datasource: GithubTagsDatasource.id,
     lookupName: 'nodejs/node',
   };
   return { deps: [dep] };
diff --git a/lib/manager/nodenv/index.ts b/lib/manager/nodenv/index.ts
index cf206a758f..1b04cf5e93 100644
--- a/lib/manager/nodenv/index.ts
+++ b/lib/manager/nodenv/index.ts
@@ -1,5 +1,5 @@
 import { ProgrammingLanguage } from '../../constants';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import * as nodeVersioning from '../../versioning/node';
 
 export { extractPackageFile } from './extract';
@@ -11,4 +11,4 @@ export const defaultConfig = {
   versioning: nodeVersioning.id,
 };
 
-export const supportedDatasources = [datasourceGithubTags.id];
+export const supportedDatasources = [GithubTagsDatasource.id];
diff --git a/lib/manager/npm/extract/index.ts b/lib/manager/npm/extract/index.ts
index 6ee99165f1..4b57e051fc 100644
--- a/lib/manager/npm/extract/index.ts
+++ b/lib/manager/npm/extract/index.ts
@@ -2,7 +2,7 @@ import is from '@sindresorhus/is';
 import validateNpmPackageName from 'validate-npm-package-name';
 import { GlobalConfig } from '../../../config/global';
 import { CONFIG_VALIDATION } from '../../../constants/error-messages';
-import * as datasourceGithubTags from '../../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../../datasource/github-tags';
 import { id as npmId } from '../../../datasource/npm';
 import { logger } from '../../../logger';
 import { getSiblingFileName, readLocalFile } from '../../../util/fs';
@@ -188,7 +188,7 @@ export async function extractPackageFile(
     dep.currentValue = input.trim();
     if (depType === 'engines' || depType === 'packageManager') {
       if (depName === 'node') {
-        dep.datasource = datasourceGithubTags.id;
+        dep.datasource = GithubTagsDatasource.id;
         dep.lookupName = 'nodejs/node';
         dep.versioning = nodeVersioning.id;
         constraints.node = dep.currentValue;
@@ -211,7 +211,7 @@ export async function extractPackageFile(
         dep.commitMessageTopic = 'pnpm';
         constraints.pnpm = dep.currentValue;
       } else if (depName === 'vscode') {
-        dep.datasource = datasourceGithubTags.id;
+        dep.datasource = GithubTagsDatasource.id;
         dep.lookupName = 'microsoft/vscode';
         constraints.vscode = dep.currentValue;
       } else {
@@ -226,7 +226,7 @@ export async function extractPackageFile(
     // support for volta
     if (depType === 'volta') {
       if (depName === 'node') {
-        dep.datasource = datasourceGithubTags.id;
+        dep.datasource = GithubTagsDatasource.id;
         dep.lookupName = 'nodejs/node';
         dep.versioning = nodeVersioning.id;
       } else if (depName === 'yarn') {
@@ -311,7 +311,7 @@ export async function extractPackageFile(
     if (isVersion(depRefPart)) {
       dep.currentRawValue = dep.currentValue;
       dep.currentValue = depRefPart;
-      dep.datasource = datasourceGithubTags.id;
+      dep.datasource = GithubTagsDatasource.id;
       dep.lookupName = githubOwnerRepo;
       dep.pinDigests = false;
     } else if (
@@ -321,7 +321,7 @@ export async function extractPackageFile(
       dep.currentRawValue = dep.currentValue;
       dep.currentValue = null;
       dep.currentDigest = depRefPart;
-      dep.datasource = datasourceGithubTags.id;
+      dep.datasource = GithubTagsDatasource.id;
       dep.lookupName = githubOwnerRepo;
     } else {
       dep.skipReason = 'unversioned-reference';
diff --git a/lib/manager/npm/index.ts b/lib/manager/npm/index.ts
index 0d5bdbf49d..653ee81edb 100644
--- a/lib/manager/npm/index.ts
+++ b/lib/manager/npm/index.ts
@@ -1,5 +1,5 @@
 import { ProgrammingLanguage } from '../../constants';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { id as npmId } from '../../datasource/npm';
 import * as npmVersioning from '../../versioning/npm';
 
@@ -31,4 +31,4 @@ export const defaultConfig = {
   },
 };
 
-export const supportedDatasources = [datasourceGithubTags.id, npmId];
+export const supportedDatasources = [GithubTagsDatasource.id, npmId];
diff --git a/lib/manager/nvm/extract.ts b/lib/manager/nvm/extract.ts
index b8b777cd14..ca3a3941cd 100644
--- a/lib/manager/nvm/extract.ts
+++ b/lib/manager/nvm/extract.ts
@@ -1,11 +1,11 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import type { PackageDependency, PackageFile } from '../types';
 
 export function extractPackageFile(content: string): PackageFile {
   const dep: PackageDependency = {
     depName: 'node',
     currentValue: content.trim(),
-    datasource: datasourceGithubTags.id,
+    datasource: GithubTagsDatasource.id,
     lookupName: 'nodejs/node',
   };
   return { deps: [dep] };
diff --git a/lib/manager/nvm/index.ts b/lib/manager/nvm/index.ts
index 337f088cac..43a744996e 100644
--- a/lib/manager/nvm/index.ts
+++ b/lib/manager/nvm/index.ts
@@ -1,5 +1,5 @@
 import { ProgrammingLanguage } from '../../constants';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import * as nodeVersioning from '../../versioning/node';
 
 export { extractPackageFile } from './extract';
@@ -12,4 +12,4 @@ export const defaultConfig = {
   pinDigests: false,
 };
 
-export const supportedDatasources = [datasourceGithubTags.id];
+export const supportedDatasources = [GithubTagsDatasource.id];
diff --git a/lib/manager/pre-commit/extract.ts b/lib/manager/pre-commit/extract.ts
index b7721e7a5c..88a1502763 100644
--- a/lib/manager/pre-commit/extract.ts
+++ b/lib/manager/pre-commit/extract.ts
@@ -1,7 +1,7 @@
 import is from '@sindresorhus/is';
 import { load } from 'js-yaml';
 import { PlatformId } from '../../constants';
-import { id as githubTagsId } from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
 import { logger } from '../../logger';
 import type { SkipReason } from '../../types';
@@ -34,7 +34,7 @@ function determineDatasource(
 ): { datasource?: string; registryUrls?: string[]; skipReason?: SkipReason } {
   if (hostname === 'github.com') {
     logger.debug({ repository, hostname }, 'Found github dependency');
-    return { datasource: githubTagsId };
+    return { datasource: GithubTagsDatasource.id };
   }
   if (hostname === 'gitlab.com') {
     logger.debug({ repository, hostname }, 'Found gitlab dependency');
@@ -52,7 +52,7 @@ function determineDatasource(
   }
   for (const [hostType, sourceId] of [
     [PlatformId.Gitea, GitlabTagsDatasource.id],
-    [PlatformId.Github, githubTagsId],
+    [PlatformId.Github, GithubTagsDatasource.id],
     [PlatformId.Gitlab, GitlabTagsDatasource.id],
   ]) {
     if (!isEmptyObject(find({ hostType, url: hostUrl }))) {
diff --git a/lib/manager/pre-commit/index.ts b/lib/manager/pre-commit/index.ts
index fae8be3dee..f52495f8a1 100644
--- a/lib/manager/pre-commit/index.ts
+++ b/lib/manager/pre-commit/index.ts
@@ -1,8 +1,11 @@
-import { id as githubTagsId } from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { GitlabTagsDatasource } from '../../datasource/gitlab-tags';
 export { extractPackageFile } from './extract';
 
-export const supportedDatasources = [githubTagsId, GitlabTagsDatasource.id];
+export const supportedDatasources = [
+  GithubTagsDatasource.id,
+  GitlabTagsDatasource.id,
+];
 
 export const defaultConfig = {
   commitMessageTopic: 'pre-commit hook {{depName}}',
diff --git a/lib/manager/terraform-version/extract.ts b/lib/manager/terraform-version/extract.ts
index 10a886481f..99dccb3db8 100644
--- a/lib/manager/terraform-version/extract.ts
+++ b/lib/manager/terraform-version/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceGitHubRelease from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { logger } from '../../logger';
 import type { PackageDependency, PackageFile } from '../types';
 
@@ -7,7 +7,7 @@ export function extractPackageFile(content: string): PackageFile {
   const dep: PackageDependency = {
     depName: 'hashicorp/terraform',
     currentValue: content.trim(),
-    datasource: datasourceGitHubRelease.id,
+    datasource: GithubReleasesDatasource.id,
   };
   return { deps: [dep] };
 }
diff --git a/lib/manager/terraform-version/index.ts b/lib/manager/terraform-version/index.ts
index 718e39968c..0f753ba3af 100644
--- a/lib/manager/terraform-version/index.ts
+++ b/lib/manager/terraform-version/index.ts
@@ -1,9 +1,9 @@
-import * as datasourceGitHubRelease from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import * as hashicorpVersioning from '../../versioning/hashicorp';
 
 export { extractPackageFile } from './extract';
 
-export const supportedDatasources = [datasourceGitHubRelease.id];
+export const supportedDatasources = [GithubReleasesDatasource.id];
 
 export const defaultConfig = {
   fileMatch: ['(^|/)\\.terraform-version$'],
diff --git a/lib/manager/terraform/index.ts b/lib/manager/terraform/index.ts
index cea16def59..61bcc7e7a3 100644
--- a/lib/manager/terraform/index.ts
+++ b/lib/manager/terraform/index.ts
@@ -1,6 +1,6 @@
 import { BitBucketTagsDatasource } from '../../datasource/bitbucket-tags';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { HelmDatasource } from '../../datasource/helm';
 import { TerraformModuleDatasource } from '../../datasource/terraform-module';
 import { TerraformProviderDatasource } from '../../datasource/terraform-provider';
@@ -12,7 +12,7 @@ export { extractPackageFile } from './extract';
 export const supportedDatasources = [
   BitBucketTagsDatasource.id,
   GitTagsDatasource.id,
-  datasourceGithubTags.id,
+  GithubTagsDatasource.id,
   HelmDatasource.id,
   TerraformModuleDatasource.id,
   TerraformProviderDatasource.id,
diff --git a/lib/manager/terraform/modules.ts b/lib/manager/terraform/modules.ts
index bf2b0b1212..18b4a10aac 100644
--- a/lib/manager/terraform/modules.ts
+++ b/lib/manager/terraform/modules.ts
@@ -1,6 +1,6 @@
 import { BitBucketTagsDatasource } from '../../datasource/bitbucket-tags';
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { TerraformModuleDatasource } from '../../datasource/terraform-module';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
@@ -42,7 +42,7 @@ export function analyseTerraformModule(dep: PackageDependency): void {
     dep.depType = 'module';
     dep.depName = 'github.com/' + dep.lookupName;
     dep.currentValue = githubRefMatch.groups.tag;
-    dep.datasource = datasourceGithubTags.id;
+    dep.datasource = GithubTagsDatasource.id;
   } else if (bitbucketRefMatch) {
     dep.depType = 'module';
     dep.depName =
diff --git a/lib/manager/terraform/required-version.ts b/lib/manager/terraform/required-version.ts
index 12f5ad4bdf..33969f823a 100644
--- a/lib/manager/terraform/required-version.ts
+++ b/lib/manager/terraform/required-version.ts
@@ -1,4 +1,4 @@
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
 import type { PackageDependency } from '../types';
@@ -47,7 +47,7 @@ export function extractTerraformRequiredVersion(
 
 export function analyseTerraformVersion(dep: PackageDependency): void {
   dep.depType = 'required_version';
-  dep.datasource = datasourceGithubTags.id;
+  dep.datasource = GithubTagsDatasource.id;
   dep.depName = 'hashicorp/terraform';
   dep.extractVersion = 'v(?<version>.*)$';
 }
diff --git a/lib/manager/terragrunt-version/extract.ts b/lib/manager/terragrunt-version/extract.ts
index c6a87213ad..fb918e0635 100644
--- a/lib/manager/terragrunt-version/extract.ts
+++ b/lib/manager/terragrunt-version/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceGitHubRelease from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import { logger } from '../../logger';
 import type { PackageDependency, PackageFile } from '../types';
 
@@ -7,7 +7,7 @@ export function extractPackageFile(content: string): PackageFile {
   const dep: PackageDependency = {
     depName: 'gruntwork-io/terragrunt',
     currentValue: content.trim(),
-    datasource: datasourceGitHubRelease.id,
+    datasource: GithubReleasesDatasource.id,
   };
   return { deps: [dep] };
 }
diff --git a/lib/manager/terragrunt-version/index.ts b/lib/manager/terragrunt-version/index.ts
index 1b6aa3b029..a1286d9721 100644
--- a/lib/manager/terragrunt-version/index.ts
+++ b/lib/manager/terragrunt-version/index.ts
@@ -1,9 +1,9 @@
-import * as datasourceGitHubRelease from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import * as hashicorpVersioning from '../../versioning/hashicorp';
 
 export { extractPackageFile } from './extract';
 
-export const supportedDatasources = [datasourceGitHubRelease.id];
+export const supportedDatasources = [GithubReleasesDatasource.id];
 
 export const defaultConfig = {
   fileMatch: ['(^|/)\\.terragrunt-version$'],
diff --git a/lib/manager/terragrunt/index.ts b/lib/manager/terragrunt/index.ts
index ac5b5a0628..126af225f0 100644
--- a/lib/manager/terragrunt/index.ts
+++ b/lib/manager/terragrunt/index.ts
@@ -1,5 +1,5 @@
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { TerraformModuleDatasource } from '../../datasource/terraform-module';
 import * as hashicorpVersioning from '../../versioning/hashicorp';
 
@@ -7,7 +7,7 @@ export { extractPackageFile } from './extract';
 
 export const supportedDatasources = [
   GitTagsDatasource.id,
-  datasourceGithubTags.id,
+  GithubTagsDatasource.id,
   TerraformModuleDatasource.id,
 ];
 
diff --git a/lib/manager/terragrunt/modules.ts b/lib/manager/terragrunt/modules.ts
index 1c1e0884f6..c76ff3c86f 100644
--- a/lib/manager/terragrunt/modules.ts
+++ b/lib/manager/terragrunt/modules.ts
@@ -1,5 +1,5 @@
 import { GitTagsDatasource } from '../../datasource/git-tags';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { TerraformModuleDatasource } from '../../datasource/terraform-module';
 import { logger } from '../../logger';
 import { regEx } from '../../util/regex';
@@ -38,7 +38,7 @@ export function analyseTerragruntModule(dep: PackageDependency): void {
     dep.lookupName = githubRefMatch.groups.project.replace(regEx(/\.git$/), '');
     dep.depName = 'github.com/' + dep.lookupName;
     dep.currentValue = githubRefMatch.groups.tag;
-    dep.datasource = datasourceGithubTags.id;
+    dep.datasource = GithubTagsDatasource.id;
   } else if (gitTagsRefMatch) {
     dep.depType = 'gitTags';
     if (gitTagsRefMatch.groups.path.includes('//')) {
diff --git a/lib/manager/travis/extract.ts b/lib/manager/travis/extract.ts
index c50ad28bf0..52a8648003 100644
--- a/lib/manager/travis/extract.ts
+++ b/lib/manager/travis/extract.ts
@@ -1,6 +1,6 @@
 import is from '@sindresorhus/is';
 import { load } from 'js-yaml';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import { logger } from '../../logger';
 import type { PackageDependency, PackageFile } from '../types';
 import type { TravisMatrixItem, TravisYaml } from './types';
@@ -19,7 +19,7 @@ export function extractPackageFile(content: string): PackageFile | null {
   if (doc && is.array(doc.node_js)) {
     deps = doc.node_js.map((currentValue) => ({
       depName: 'node',
-      datasource: datasourceGithubTags.id,
+      datasource: GithubTagsDatasource.id,
       lookupName: 'nodejs/node',
       currentValue: currentValue.toString(),
     }));
@@ -43,7 +43,7 @@ export function extractPackageFile(content: string): PackageFile | null {
         item.node_js.forEach((currentValue) => {
           deps.push({
             depName: 'node',
-            datasource: datasourceGithubTags.id,
+            datasource: GithubTagsDatasource.id,
             lookupName: 'nodejs/node',
             currentValue: currentValue.toString(),
           });
@@ -51,7 +51,7 @@ export function extractPackageFile(content: string): PackageFile | null {
       } else if (is.string(item.node_js)) {
         deps.push({
           depName: 'node',
-          datasource: datasourceGithubTags.id,
+          datasource: GithubTagsDatasource.id,
           lookupName: 'nodejs/node',
           currentValue: item.node_js.toString(),
         });
diff --git a/lib/manager/travis/index.ts b/lib/manager/travis/index.ts
index ee13640188..1fc8e41c03 100644
--- a/lib/manager/travis/index.ts
+++ b/lib/manager/travis/index.ts
@@ -1,12 +1,12 @@
 import { ProgrammingLanguage } from '../../constants';
-import * as datasourceGithubTags from '../../datasource/github-tags';
+import { GithubTagsDatasource } from '../../datasource/github-tags';
 import * as nodeVersioning from '../../versioning/node';
 
 export { extractPackageFile } from './extract';
 
 export const language = ProgrammingLanguage.NodeJS;
 
-export const supportedDatasources = [datasourceGithubTags.id];
+export const supportedDatasources = [GithubTagsDatasource.id];
 
 export const defaultConfig = {
   fileMatch: ['^.travis.yml$'],
diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts
index cbb70841a3..a298cfcafc 100644
--- a/lib/util/http/github.spec.ts
+++ b/lib/util/http/github.spec.ts
@@ -8,7 +8,7 @@ import {
   PLATFORM_RATE_LIMIT_EXCEEDED,
   REPOSITORY_CHANGED,
 } from '../../constants/error-messages';
-import { id as GITHUB_RELEASES_ID } from '../../datasource/github-releases';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
 import * as _repositoryCache from '../../util/cache/repository';
 import type { Cache } from '../../util/cache/repository/types';
 import * as hostRules from '../host-rules';
@@ -77,10 +77,10 @@ describe('util/http/github', () => {
     });
 
     it('supports different datasources', async () => {
-      const githubApiDatasource = new GithubHttp(GITHUB_RELEASES_ID);
+      const githubApiDatasource = new GithubHttp(GithubReleasesDatasource.id);
       hostRules.add({ hostType: 'github', token: 'abc' });
       hostRules.add({
-        hostType: GITHUB_RELEASES_ID,
+        hostType: GithubReleasesDatasource.id,
         token: 'def',
       });
       httpMock.scope(githubApiHost).get('/some-url').reply(200);
diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts
index 1afcb616b0..ffb3dd45a4 100644
--- a/lib/workers/repository/process/lookup/index.spec.ts
+++ b/lib/workers/repository/process/lookup/index.spec.ts
@@ -10,8 +10,8 @@ import * as datasourceDocker from '../../../../datasource/docker';
 import { id as datasourceDockerId } from '../../../../datasource/docker';
 import { GitRefsDatasource } from '../../../../datasource/git-refs';
 import { GitDatasource } from '../../../../datasource/git-refs/base';
-import * as datasourceGithubReleases from '../../../../datasource/github-releases';
-import { id as datasourceGithubTagsId } from '../../../../datasource/github-tags';
+import { GithubReleasesDatasource } from '../../../../datasource/github-releases';
+import { GithubTagsDatasource } from '../../../../datasource/github-tags';
 import { id as datasourceNpmId } from '../../../../datasource/npm';
 import { PackagistDatasource } from '../../../../datasource/packagist';
 import { PypiDatasource } from '../../../../datasource/pypi';
@@ -24,7 +24,6 @@ import type { LookupUpdateConfig } from './types';
 import * as lookup from '.';
 
 jest.mock('../../../../datasource/docker');
-jest.mock('../../../../datasource/github-releases');
 
 const fixtureRoot = '../../../../config/npm';
 const qJson = {
@@ -41,9 +40,6 @@ const webpackJson = loadJsonFixture('webpack.json', fixtureRoot);
 
 const docker = mocked(datasourceDocker) as any;
 docker.defaultRegistryUrls = ['https://index.docker.io'];
-const githubReleases = mocked(datasourceGithubReleases);
-
-Object.assign(githubReleases, { defaultRegistryUrls: ['https://github.com'] });
 
 let config: LookupUpdateConfig;
 
@@ -813,21 +809,15 @@ describe('workers/repository/process/lookup/index', () => {
     it('should ignore unstable versions from datasource', async () => {
       config.currentValue = '1.4.4';
       config.depName = 'some/action';
-      config.datasource = datasourceGithubReleases.id;
-      githubReleases.getReleases.mockResolvedValueOnce({
-        releases: [
-          {
-            version: '1.4.4',
-          },
-          {
-            version: '2.0.0',
-          },
-          {
-            version: '2.1.0',
-            isStable: false,
-          },
-        ],
-      });
+      config.datasource = GithubReleasesDatasource.id;
+      httpMock
+        .scope('https://api.github.com')
+        .get('/repos/some/action/releases?per_page=100')
+        .reply(200, [
+          { tag_name: '1.4.4' },
+          { tag_name: '2.0.0' },
+          { tag_name: '2.1.0', prerelease: true },
+        ]);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot([
         { newValue: '2.0.0', updateType: 'major' },
       ]);
@@ -836,28 +826,21 @@ describe('workers/repository/process/lookup/index', () => {
     it('should return pendingChecks', async () => {
       config.currentValue = '1.4.4';
       config.depName = 'some/action';
-      config.datasource = datasourceGithubReleases.id;
+      config.datasource = GithubReleasesDatasource.id;
       config.stabilityDays = 14;
       config.internalChecksFilter = 'strict';
       const yesterday = new Date();
       yesterday.setDate(yesterday.getDate() - 1);
       const lastWeek = new Date();
       lastWeek.setDate(lastWeek.getDate() - 7);
-      githubReleases.getReleases.mockResolvedValueOnce({
-        releases: [
-          {
-            version: '1.4.4',
-          },
-          {
-            version: '1.4.5',
-            releaseTimestamp: lastWeek.toISOString(),
-          },
-          {
-            version: '1.4.6',
-            releaseTimestamp: yesterday.toISOString(),
-          },
-        ],
-      });
+      httpMock
+        .scope('https://api.github.com')
+        .get('/repos/some/action/releases?per_page=100')
+        .reply(200, [
+          { tag_name: '1.4.4' },
+          { tag_name: '1.4.5', published_at: lastWeek.toISOString() },
+          { tag_name: '1.4.6', published_at: yesterday.toISOString() },
+        ]);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toHaveLength(1);
       expect(res.updates[0].newVersion).toBe('1.4.6');
@@ -867,28 +850,21 @@ describe('workers/repository/process/lookup/index', () => {
     it('should return pendingVersions', async () => {
       config.currentValue = '1.4.4';
       config.depName = 'some/action';
-      config.datasource = datasourceGithubReleases.id;
+      config.datasource = GithubReleasesDatasource.id;
       config.stabilityDays = 3;
       config.internalChecksFilter = 'strict';
       const yesterday = new Date();
       yesterday.setDate(yesterday.getDate() - 1);
       const lastWeek = new Date();
       lastWeek.setDate(lastWeek.getDate() - 7);
-      githubReleases.getReleases.mockResolvedValueOnce({
-        releases: [
-          {
-            version: '1.4.4',
-          },
-          {
-            version: '1.4.5',
-            releaseTimestamp: lastWeek.toISOString(),
-          },
-          {
-            version: '1.4.6',
-            releaseTimestamp: yesterday.toISOString(),
-          },
-        ],
-      });
+      httpMock
+        .scope('https://api.github.com')
+        .get('/repos/some/action/releases?per_page=100')
+        .reply(200, [
+          { tag_name: '1.4.4' },
+          { tag_name: '1.4.5', published_at: lastWeek.toISOString() },
+          { tag_name: '1.4.6', published_at: yesterday.toISOString() },
+        ]);
       const res = await lookup.lookupUpdates(config);
       expect(res.updates).toHaveLength(1);
       expect(res.updates[0].newVersion).toBe('1.4.5');
@@ -1201,7 +1177,7 @@ describe('workers/repository/process/lookup/index', () => {
     });
     it('handles github 404', async () => {
       config.depName = 'foo';
-      config.datasource = datasourceGithubTagsId;
+      config.datasource = GithubTagsDatasource.id;
       config.packageFile = 'package.json';
       config.currentValue = '1.0.0';
       httpMock.scope('https://pypi.org').get('/pypi/foo/json').reply(404);
-- 
GitLab