Skip to content

Commit

Permalink
feat(manager): add support for Kotlin Script (#16684)
Browse files Browse the repository at this point in the history
  • Loading branch information
krzema12 committed Jul 26, 2022
1 parent 90f85b9 commit adccb9c
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/modules/manager/api.ts
Expand Up @@ -40,6 +40,7 @@ import * as homebrew from './homebrew';
import * as html from './html';
import * as jenkins from './jenkins';
import * as jsonnetBundler from './jsonnet-bundler';
import * as kotlinScript from './kotlin-script';
import * as kubernetes from './kubernetes';
import * as kustomize from './kustomize';
import * as leiningen from './leiningen';
Expand Down Expand Up @@ -117,6 +118,7 @@ api.set('homebrew', homebrew);
api.set('html', html);
api.set('jenkins', jenkins);
api.set('jsonnet-bundler', jsonnetBundler);
api.set('kotlin-script', kotlinScript);
api.set('kubernetes', kubernetes);
api.set('kustomize', kustomize);
api.set('leiningen', leiningen);
Expand Down
@@ -0,0 +1,10 @@
#!/usr/bin/env kotlin
@file:Repository("https://jitpack.io")
@file:DependsOn("it.krzeminski:github-actions-kotlin-dsl:0.22.0")
@file:DependsOn("org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r")
@file:Repository("https://some.other.repo/foo/bar/baz")
@file:Repository("")

// ...

println("Hello world")
@@ -0,0 +1,15 @@
#!/usr/bin/env kotlin
@file:DependsOn("it.krzeminski:github-actions-kotlin-dsl:0.22.0")
@file:DependsOn(
"org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r"
)

@file : DependsOn
(
"org.jetbrains.lets-plot:lets-plot-kotlin-jvm:3.0.2"

)

// ...

println("Hello world")
@@ -0,0 +1,9 @@
#!/usr/bin/env kotlin
@file:DependsOn("it.krzeminski:github-actions-kotlin-dsl:0.22.0")
@file:DependsOn(":org.eclipse.jgit:4.6.0.201612231935-r")
@file:DependsOn("org.jetbrains.lets-plot::3.0.2")
@file:DependsOn("org.jetbrains.lets-plot:lets-plot-kotlin-jvm:")

// ...

println("Hello world")
98 changes: 98 additions & 0 deletions lib/modules/manager/kotlin-script/extract.spec.ts
@@ -0,0 +1,98 @@
import { Fixtures } from '../../../../test/fixtures';
import { extractPackageFile } from '.';

const genericCaseFileContent = Fixtures.get('generic-case.main.kts');
const customRepositoriesFileContent = Fixtures.get(
'custom-repositories.main.kts'
);
const missingPartsFileContent = Fixtures.get('missing-parts.main.kts');

describe('modules/manager/kotlin-script/extract', () => {
describe('extractPackageFile()', () => {
it('extracts dependencies in a generic case', () => {
// when
const packageFile = extractPackageFile(genericCaseFileContent);

// then
expect(packageFile).toEqual({
deps: [
{
depName: 'it.krzeminski:github-actions-kotlin-dsl',
currentValue: '0.22.0',
replaceString: '"it.krzeminski:github-actions-kotlin-dsl:0.22.0"',
datasource: 'maven',
},
{
depName: 'org.eclipse.jgit:org.eclipse.jgit',
currentValue: '4.6.0.201612231935-r',
replaceString:
'"org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r"',
datasource: 'maven',
},
{
depName: 'org.jetbrains.lets-plot:lets-plot-kotlin-jvm',
currentValue: '3.0.2',
replaceString:
'"org.jetbrains.lets-plot:lets-plot-kotlin-jvm:3.0.2"',
datasource: 'maven',
},
],
});
});

it('detects custom repository definitions', () => {
// when
const packageFile = extractPackageFile(customRepositoriesFileContent);

// then
expect(packageFile).toEqual({
deps: [
{
depName: 'it.krzeminski:github-actions-kotlin-dsl',
currentValue: '0.22.0',
replaceString: '"it.krzeminski:github-actions-kotlin-dsl:0.22.0"',
datasource: 'maven',
},
{
depName: 'org.eclipse.jgit:org.eclipse.jgit',
currentValue: '4.6.0.201612231935-r',
replaceString:
'"org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r"',
datasource: 'maven',
},
],
registryUrls: [
'https://jitpack.io',
'https://some.other.repo/foo/bar/baz',
],
});
});

it('no dependencies', () => {
// when
const packageFile = extractPackageFile(`
#!/usr/bin/env kotlin
println("Hello world")`);

// then
expect(packageFile).toBeNull();
});

it('skips dependencies with missing parts', () => {
// when
const packageFile = extractPackageFile(missingPartsFileContent);

// then
expect(packageFile).toEqual({
deps: [
{
depName: 'it.krzeminski:github-actions-kotlin-dsl',
currentValue: '0.22.0',
replaceString: '"it.krzeminski:github-actions-kotlin-dsl:0.22.0"',
datasource: 'maven',
},
],
});
});
});
});
40 changes: 40 additions & 0 deletions lib/modules/manager/kotlin-script/extract.ts
@@ -0,0 +1,40 @@
import is from '@sindresorhus/is';
import { regEx } from '../../../util/regex';
import { MavenDatasource } from '../../datasource/maven';
import type { PackageDependency, PackageFile } from '../types';

const dependsOnRegex = regEx(
/@file\s*:\s*DependsOn\s*\(\s*(?<replaceString>"(?<groupId>.+):(?<artifactId>.+):(?<version>.+)")\s*\)/g
);
const repositoryRegex = regEx(
/@file\s*:\s*Repository\s*\(\s*"(?<repositoryName>.+)"\s*\)/g
);

export function extractPackageFile(fileContent: string): PackageFile | null {
const registryUrls: string[] = [...fileContent.matchAll(repositoryRegex)]
.map((match) => match.groups?.repositoryName)
.filter(is.string);

const matches = [...fileContent.matchAll(dependsOnRegex)]
.map((m) => m.groups)
.filter(is.truthy);
const deps: PackageDependency[] = [];
for (const match of matches) {
const dep: PackageDependency = {
currentValue: match.version,
depName: `${match.groupId}:${match.artifactId}`,
replaceString: match.replaceString,
datasource: MavenDatasource.id,
};
deps.push(dep);
}

if (deps.length === 0) {
return null;
}

return {
deps,
...(registryUrls.length && { registryUrls }),
};
}
11 changes: 11 additions & 0 deletions lib/modules/manager/kotlin-script/index.spec.ts
@@ -0,0 +1,11 @@
import { regEx } from '../../../util/regex';
import { defaultConfig } from '.';

describe('modules/manager/kotlin-script/index', () => {
it('fileMatch regex is correct', () => {
expect(defaultConfig.fileMatch).toHaveLength(1);
defaultConfig.fileMatch.forEach((pattern) => {
expect(() => regEx(pattern)).not.toThrow();
});
});
});
9 changes: 9 additions & 0 deletions lib/modules/manager/kotlin-script/index.ts
@@ -0,0 +1,9 @@
import { MavenDatasource } from '../../datasource/maven';

export { extractPackageFile } from './extract';

export const defaultConfig = {
fileMatch: ['^.+\\.main\\.kts$'],
};

export const supportedDatasources = [MavenDatasource.id];
32 changes: 32 additions & 0 deletions lib/modules/manager/kotlin-script/readme.md
@@ -0,0 +1,32 @@
---
title: Kotlin Script dependency versions
description: Kotlin Script dependency versions support in Renovate
---

Renovate supports upgrading dependencies in [Kotlin Script](https://github.com/Kotlin/KEEP/blob/master/proposals/scripting-support.md) files.
These are self-contained scripts where one can write Kotlin code with JVM backend, and compilation happens when the
scripts are ran. For example:

```kotlin
#!/usr/bin/env kotlin
@file:Repository("https://jitpack.io")
@file:DependsOn("com.github.krzema12:github-actions-kotlin-dsl:main-SNAPSHOT")
@file:DependsOn("org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r")
@file:DependsOn("org.jetbrains.lets-plot:lets-plot-kotlin-jvm:3.0.2")

println("Hello world!")

// ...
```

By default, Renovate scans files only with `.main.kts` extension and not `.kts`, to avoid ambiguity with Gradle config
files that have `.gradle.kts` extension. As there are cases where just `.kts` extension or no extension is used,
Renovate can be [configured](https://docs.renovatebot.com/configuration-options/) to scan also these:

```json
{
"kotlin-script": {
"fileMatch": ["^.*\\.kts$"]
}
}
```

0 comments on commit adccb9c

Please sign in to comment.