From e5049e490477e87df126ee2c6099a095b92cbab2 Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Mon, 12 Apr 2021 23:51:48 +0400
Subject: [PATCH] feat(cake): Add support for Cake manager (#9512)

---
 lib/manager/api.ts                            |  2 +
 lib/manager/cake/__fixtures__/build.cake      | 10 +++
 .../cake/__snapshots__/index.spec.ts.snap     | 27 ++++++++
 lib/manager/cake/index.spec.ts                | 14 ++++
 lib/manager/cake/index.ts                     | 66 +++++++++++++++++++
 lib/manager/cake/readme.md                    |  1 +
 6 files changed, 120 insertions(+)
 create mode 100644 lib/manager/cake/__fixtures__/build.cake
 create mode 100644 lib/manager/cake/__snapshots__/index.spec.ts.snap
 create mode 100644 lib/manager/cake/index.spec.ts
 create mode 100644 lib/manager/cake/index.ts
 create mode 100644 lib/manager/cake/readme.md

diff --git a/lib/manager/api.ts b/lib/manager/api.ts
index 7abc198d4f..0a1a2c49d2 100644
--- a/lib/manager/api.ts
+++ b/lib/manager/api.ts
@@ -6,6 +6,7 @@ import * as batectWrapper from './batect-wrapper';
 import * as bazel from './bazel';
 import * as buildkite from './buildkite';
 import * as bundler from './bundler';
+import * as cake from './cake';
 import * as cargo from './cargo';
 import * as cdnurl from './cdnurl';
 import * as circleci from './circleci';
@@ -70,6 +71,7 @@ api.set('batect-wrapper', batectWrapper);
 api.set('bazel', bazel);
 api.set('buildkite', buildkite);
 api.set('bundler', bundler);
+api.set('cake', cake);
 api.set('cargo', cargo);
 api.set('cdnurl', cdnurl);
 api.set('circleci', circleci);
diff --git a/lib/manager/cake/__fixtures__/build.cake b/lib/manager/cake/__fixtures__/build.cake
new file mode 100644
index 0000000000..4cdbf4d049
--- /dev/null
+++ b/lib/manager/cake/__fixtures__/build.cake
@@ -0,0 +1,10 @@
+foo
+#addin nuget:?package=Foo.Foo&version=1.1.1
+#tool nuget:https://example.com?package=Bar.Bar&version=2.2.2
+#module nuget:file:///tmp/?package=Baz.Baz&version=3.3.3
+// #module nuget:?package=Qux.Qux&version=4.4.4
+/*
+#module nuget:?package=Quux.Quux&version=5.5.5
+*/
+bar
+#module nuget:foobar!@#
diff --git a/lib/manager/cake/__snapshots__/index.spec.ts.snap b/lib/manager/cake/__snapshots__/index.spec.ts.snap
new file mode 100644
index 0000000000..a6e389b1fc
--- /dev/null
+++ b/lib/manager/cake/__snapshots__/index.spec.ts.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`manager/cake/index extracts 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "1.1.1",
+      "datasource": "nuget",
+      "depName": "Foo.Foo",
+    },
+    Object {
+      "currentValue": "2.2.2",
+      "datasource": "nuget",
+      "depName": "Bar.Bar",
+      "registryUrls": Array [
+        "https://example.com",
+      ],
+    },
+    Object {
+      "currentValue": "3.3.3",
+      "datasource": "nuget",
+      "depName": "Baz.Baz",
+      "skipReason": "unsupported-url",
+    },
+  ],
+}
+`;
diff --git a/lib/manager/cake/index.spec.ts b/lib/manager/cake/index.spec.ts
new file mode 100644
index 0000000000..c354776c5e
--- /dev/null
+++ b/lib/manager/cake/index.spec.ts
@@ -0,0 +1,14 @@
+import { readFileSync } from 'fs';
+import { getName } from '../../../test/util';
+import { extractPackageFile } from '.';
+
+const content = readFileSync(
+  'lib/manager/cake/__fixtures__/build.cake',
+  'utf8'
+);
+
+describe(getName(__filename), () => {
+  it('extracts', () => {
+    expect(extractPackageFile(content)).toMatchSnapshot();
+  });
+});
diff --git a/lib/manager/cake/index.ts b/lib/manager/cake/index.ts
new file mode 100644
index 0000000000..60cb08b985
--- /dev/null
+++ b/lib/manager/cake/index.ts
@@ -0,0 +1,66 @@
+import moo from 'moo';
+import { LANGUAGE_DOT_NET } from '../../constants/languages';
+import { id as datasource } from '../../datasource/nuget';
+import { SkipReason } from '../../types';
+import { PackageDependency, PackageFile } from '../types';
+
+export const language = LANGUAGE_DOT_NET;
+
+export const defaultConfig = {
+  fileMatch: ['\\.cake$'],
+};
+
+const lexerStates = {
+  main: {
+    lineComment: { match: /\/\/.*?$/ },
+    multiLineComment: { match: /\/\*[^]*?\*\//, lineBreaks: true },
+    dependency: {
+      match: /^#(?:addin|tool|module)\s+(?:nuget|dotnet):.*$/,
+    },
+    unknown: { match: /[^]/, lineBreaks: true },
+  },
+};
+
+function parseDependencyLine(line: string): PackageDependency | null {
+  try {
+    let url = line.replace(/^[^:]*:/, '');
+    const isEmptyHost = url.startsWith('?');
+    url = isEmptyHost ? `http://localhost/${url}` : url;
+    const { origin: registryUrl, protocol, searchParams } = new URL(url);
+
+    const depName = searchParams.get('package');
+    const currentValue = searchParams.get('version');
+
+    const result: PackageDependency = { datasource, depName, currentValue };
+
+    if (!isEmptyHost) {
+      if (protocol.startsWith('http')) {
+        result.registryUrls = [registryUrl];
+      } else {
+        result.skipReason = SkipReason.UnsupportedUrl;
+      }
+    }
+
+    return result;
+  } catch (err) {
+    return null;
+  }
+}
+
+export function extractPackageFile(content: string): PackageFile {
+  const deps = [];
+  const lexer = moo.states(lexerStates);
+  lexer.reset(content);
+  let token = lexer.next();
+  while (token) {
+    const { type, value } = token;
+    if (type === 'dependency') {
+      const dep = parseDependencyLine(value);
+      if (dep) {
+        deps.push(dep);
+      }
+    }
+    token = lexer.next();
+  }
+  return { deps };
+}
diff --git a/lib/manager/cake/readme.md b/lib/manager/cake/readme.md
new file mode 100644
index 0000000000..37d6820850
--- /dev/null
+++ b/lib/manager/cake/readme.md
@@ -0,0 +1 @@
+Extracts dependencies from `*.cake` files.
-- 
GitLab