From 1119d609fd20245209778d6d9b918a396a93d287 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?= <gjourdanweil@gmail.com>
Date: Mon, 1 Apr 2024 15:02:27 +0200
Subject: [PATCH] feat(manager/scalafmt): implement manager (#28192)

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
---
 lib/modules/manager/api.ts                   |  2 ++
 lib/modules/manager/scalafmt/extract.spec.ts | 38 ++++++++++++++++++++
 lib/modules/manager/scalafmt/extract.ts      | 30 ++++++++++++++++
 lib/modules/manager/scalafmt/index.ts        | 12 +++++++
 lib/modules/manager/scalafmt/readme.md       |  3 ++
 5 files changed, 85 insertions(+)
 create mode 100644 lib/modules/manager/scalafmt/extract.spec.ts
 create mode 100644 lib/modules/manager/scalafmt/extract.ts
 create mode 100644 lib/modules/manager/scalafmt/index.ts
 create mode 100644 lib/modules/manager/scalafmt/readme.md

diff --git a/lib/modules/manager/api.ts b/lib/modules/manager/api.ts
index 94821d957d..f24d2b00d6 100644
--- a/lib/modules/manager/api.ts
+++ b/lib/modules/manager/api.ts
@@ -75,6 +75,7 @@ import * as puppet from './puppet';
 import * as pyenv from './pyenv';
 import * as rubyVersion from './ruby-version';
 import * as sbt from './sbt';
+import * as scalafmt from './scalafmt';
 import * as setupCfg from './setup-cfg';
 import * as swift from './swift';
 import * as tekton from './tekton';
@@ -169,6 +170,7 @@ api.set('puppet', puppet);
 api.set('pyenv', pyenv);
 api.set('ruby-version', rubyVersion);
 api.set('sbt', sbt);
+api.set('scalafmt', scalafmt);
 api.set('setup-cfg', setupCfg);
 api.set('swift', swift);
 api.set('tekton', tekton);
diff --git a/lib/modules/manager/scalafmt/extract.spec.ts b/lib/modules/manager/scalafmt/extract.spec.ts
new file mode 100644
index 0000000000..f371daa748
--- /dev/null
+++ b/lib/modules/manager/scalafmt/extract.spec.ts
@@ -0,0 +1,38 @@
+import { codeBlock } from 'common-tags';
+import { extractPackageFile } from './extract';
+
+describe('modules/manager/scalafmt/extract', () => {
+  describe('extractPackageFile()', () => {
+    it('extracts version correctly', () => {
+      const scalafmtConf = codeBlock`
+      version = 3.8.0
+    `;
+      const packages = extractPackageFile(scalafmtConf);
+      expect(packages).toMatchObject({
+        deps: [
+          {
+            datasource: 'github-releases',
+            packageName: 'scalameta/scalafmt',
+            depName: 'scalafmt',
+            currentValue: '3.8.0',
+            versioning: 'semver',
+            extractVersion: '^v(?<version>\\S+)',
+          },
+        ],
+      });
+    });
+
+    it('ignore file if no version specified', () => {
+      const scalafmtConf = codeBlock`
+      maxColumn = 80
+    `;
+      const packages = extractPackageFile(scalafmtConf);
+      expect(packages).toBeNull();
+    });
+
+    it('should return empty packagefiles is no content is provided', () => {
+      const packages = extractPackageFile('');
+      expect(packages).toBeNull();
+    });
+  });
+});
diff --git a/lib/modules/manager/scalafmt/extract.ts b/lib/modules/manager/scalafmt/extract.ts
new file mode 100644
index 0000000000..0c484b2dd8
--- /dev/null
+++ b/lib/modules/manager/scalafmt/extract.ts
@@ -0,0 +1,30 @@
+import { regEx } from '../../../util/regex';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
+import * as semverVersioning from '../../versioning/semver';
+import type { PackageDependency, PackageFileContent } from '../types';
+
+const scalafmtVersionRegex = regEx(
+  'version *= *(?<version>\\d+\\.\\d+\\.\\d+)',
+);
+
+export function extractPackageFile(content: string): PackageFileContent | null {
+  const regexResult = scalafmtVersionRegex.exec(content);
+  const scalafmtVersion = regexResult?.groups?.version;
+
+  if (!scalafmtVersion) {
+    return null;
+  }
+
+  const scalafmtDependency: PackageDependency = {
+    datasource: GithubReleasesDatasource.id,
+    depName: 'scalafmt',
+    packageName: 'scalameta/scalafmt',
+    versioning: semverVersioning.id,
+    currentValue: scalafmtVersion,
+    extractVersion: '^v(?<version>\\S+)',
+  };
+
+  return {
+    deps: [scalafmtDependency],
+  };
+}
diff --git a/lib/modules/manager/scalafmt/index.ts b/lib/modules/manager/scalafmt/index.ts
new file mode 100644
index 0000000000..25b512b938
--- /dev/null
+++ b/lib/modules/manager/scalafmt/index.ts
@@ -0,0 +1,12 @@
+import type { Category } from '../../../constants';
+import { GithubReleasesDatasource } from '../../datasource/github-releases';
+
+export { extractPackageFile } from './extract';
+
+export const supportedDatasources = [GithubReleasesDatasource.id];
+
+export const defaultConfig = {
+  fileMatch: ['(^|/)\\.scalafmt.conf$'],
+};
+
+export const categories: Category[] = ['java'];
diff --git a/lib/modules/manager/scalafmt/readme.md b/lib/modules/manager/scalafmt/readme.md
new file mode 100644
index 0000000000..517b7966b0
--- /dev/null
+++ b/lib/modules/manager/scalafmt/readme.md
@@ -0,0 +1,3 @@
+Extracts scalafmt version from `.scalafmt.conf` file.
+
+New versions of Scalafmt are looked up on Github Releases.
-- 
GitLab