Skip to content

Commit

Permalink
feat: maven kotlin multiplatform support (#412)
Browse files Browse the repository at this point in the history
* Implement klib support

* Change classifier

* Use existsSync

* Add more side artifacts

* Fix casing of file names

* Add target config for kmp

* Format code

* Separate default maven pom dist and kmp pom dist

* Fix failing tests

* Make kotlin multiplatform target config optional

* Separate upload kmp pom distribution to another function

* Fix build error

* Update README to include Kotlin Multiplatform

* Add tests

* Fix test

* Fix test

* fix transforming side artifacts

* change kotlinMultiplatform to kmp
  • Loading branch information
buenaflor authored Mar 13, 2023
1 parent b298f7b commit 8f84181
Show file tree
Hide file tree
Showing 3 changed files with 372 additions and 29 deletions.
35 changes: 27 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1045,14 +1045,16 @@ Note: in order to see the output of the commands, set the [logging level](#loggi
**Configuration**
| Option | Description |
| ------------------- | -------------------------------------------------------------------- |
| `mavenCliPath` | Path to the Maven CLI. It must be executable by the calling process. |
| `mavenSettingsPath` | Path to the Maven `settings.xml` file. |
| `mavenRepoId` | ID of the Maven server in the `settings.xml`. |
| `mavenRepoUrl` | URL of the Maven repository. |
| `android` | Android configuration, see below. |
| Option | Description |
| --------------------- | -------------------------------------------------------------------- |
| `mavenCliPath` | Path to the Maven CLI. It must be executable by the calling process. |
| `mavenSettingsPath` | Path to the Maven `settings.xml` file. |
| `mavenRepoId` | ID of the Maven server in the `settings.xml`. |
| `mavenRepoUrl` | URL of the Maven repository. |
| `android` | Android configuration, see below. |
| `kmp` | Kotlin Multiplatform configuration, see below. |
The Kotlin Multiplatform configuration is optional and `false` by default.
If your project isn't related to Android, you don't need this configuration and
can set the option to `false`. If not, set the following nested elements:
Expand Down Expand Up @@ -1087,6 +1089,23 @@ targets:
fileReplacerStr: release.aar
```
**Example (with Kotlin Multiplatform config)**
```yaml
targets:
- name: maven
mavenCliPath: scripts/mvnw.cmd
mavenSettingsPath: scripts/settings.xml
mavenRepoId: ossrh
mavenRepoUrl: https://oss.sonatype.org/service/local/staging/deploy/maven2/
android:
distDirRegex: /^sentry-android-.*$/
fileReplaceeRegex: /\d\.\d\.\d(-SNAPSHOT)?/
fileReplacerStr: release.aar
kmp:
rootDistDirRegex: /sentry-kotlin-multiplatform-[0-9]+.*$/
appleDistDirRegex: /sentry-kotlin-multiplatform-(macos|ios|tvos|watchos).*/
```
### Symbol Collector (`symbol-collector`)
Using the [`symbol-collector`](https://github.com/getsentry/symbol-collector) client, uploads native symbols.
Expand Down
120 changes: 120 additions & 0 deletions src/targets/__tests__/maven.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ function getFullTargetConfig(): any {
fileReplaceeRegex: '/replacee/',
fileReplacerStr: 'replacer',
},
kmp: {
rootDistDirRegex: '/distDir/',
appleDistDirRegex: '/apple-distDir/',
},
};
}

Expand All @@ -84,6 +88,7 @@ function getRequiredTargetConfig(): any {
mavenRepoId: DEFAULT_OPTION_VALUE,
mavenRepoUrl: DEFAULT_OPTION_VALUE,
android: false,
kmp: false,
};
}

Expand Down Expand Up @@ -140,6 +145,12 @@ describe('Maven target configuration', () => {
);
});

test('no kotlinMultiplatform config', () => {
const config = getRequiredTargetConfig();
delete config.kmp;
expect(() => createMavenTarget(config)).not.toThrowError();
});

test('incorrect one-line android config', () => {
const config = getRequiredTargetConfig();
config.android = 'yes';
Expand All @@ -148,12 +159,26 @@ describe('Maven target configuration', () => {
);
});

test('incorrect one-line kotlinMultiplatform config', () => {
const config = getRequiredTargetConfig();
config.kmp = 'yes';
expect(() => createMavenTarget(config)).toThrowErrorMatchingInlineSnapshot(
`"Required root configuration for Kotlin Multiplatform is incorrect. See the documentation for more details."`
);
});

test('correct one-line android config', () => {
const config = getRequiredTargetConfig();
const mvnTarget = createMavenTarget(config);
expect(mvnTarget.mavenConfig.android).toStrictEqual(config.android);
});

test('correct one-line kotlinMultiplatform config', () => {
const config = getRequiredTargetConfig();
const mvnTarget = createMavenTarget(config);
expect(mvnTarget.mavenConfig.kmp).toStrictEqual(config.kmp);
});

test('incorrect object android config, missing prop', () => {
const config = getFullTargetConfig();
delete config.android.distDirRegex;
Expand All @@ -162,6 +187,24 @@ describe('Maven target configuration', () => {
);
});

test('incorrect object kotlinMultiplatform config, replaced root distDir', () => {
const config = getFullTargetConfig();
delete config.kmp.rootDistDirRegex;
config.kmp.anotherParam = 'unused';
expect(() => createMavenTarget(config)).toThrowErrorMatchingInlineSnapshot(
`"Required root configuration for Kotlin Multiplatform is incorrect. See the documentation for more details."`
);
});

test('incorrect object kotlinMultiplatform config, replaced apple distDir', () => {
const config = getFullTargetConfig();
delete config.kmp.appleDistDirRegex;
config.kmp.anotherParam = 'unused';
expect(() => createMavenTarget(config)).toThrowErrorMatchingInlineSnapshot(
`"Required apple configuration for Kotlin Multiplatform is incorrect. See the documentation for more details."`
);
});

test('incorrect object android config, replaced prop', () => {
const config = getFullTargetConfig();
delete config.android.distDirRegex;
Expand All @@ -180,6 +223,15 @@ describe('Maven target configuration', () => {
expect(androidConfig.additionalProp).not.toBeDefined();
});

test('correct object kotlinMultiplatform config, with additional props', () => {
const config = getFullTargetConfig();
config.kmp.additionalProp = 'not relevant';
const mvnTarget = createMavenTarget(config);
const kotlinMultiplatformConfig: any = mvnTarget.mavenConfig.kmp;
expect(config.kmp).toMatchObject(kotlinMultiplatformConfig);
expect(kotlinMultiplatformConfig.additionalProp).not.toBeDefined();
});

test('minimum required options', () => {
const mvnTarget = createMavenTarget(getRequiredTargetConfig());
targetOptions.map(secret =>
Expand All @@ -204,6 +256,8 @@ describe('Maven target configuration', () => {
expect(typeof mvnTarget.config.android.distDirRegex).toBe('string');
expect(typeof mvnTarget.config.android.fileReplaceeRegex).toBe('string');
expect(typeof mvnTarget.config.android.fileReplacerStr).toBe('string');
expect(typeof mvnTarget.config.kmp.rootDistDirRegex).toBe('string');
expect(typeof mvnTarget.config.kmp.appleDistDirRegex).toBe('string');
});

test('import GPG private key if one is present in the environment', () => {
Expand Down Expand Up @@ -231,6 +285,72 @@ describe('publish', () => {
});
});

describe('transform KMP artifacts', () => {
const tmpDirName = 'tmpDir';

test('transform apple target side artifacts', async () => {
(withTempDir as jest.MockedFunction<typeof withTempDir>).mockImplementation(
async cb => {
return await cb(tmpDirName);
}
);

const mvnTarget = createMavenTarget(getFullTargetConfig());
const files: Record<string, string | string[]> = {
javadocFile: `${tmpDirName}-javadoc.jar`,
sourcesFile: `${tmpDirName}-sources.jar`,
klibFiles: [
`${tmpDirName}-cinterop-Sentry.NSException.klib`,
`${tmpDirName}-cinterop-Sentry.klib`,
],
allFile: '',
metadataFile: `${tmpDirName}-metadata.jar`,
moduleFile: `${tmpDirName}.module`,
};
const {
sideArtifacts,
classifiers,
types,
} = mvnTarget.transformKmpSideArtifacts(false, true, files);
expect(sideArtifacts).toEqual(
`${files.javadocFile},${files.sourcesFile},${files.klibFiles},${files.metadataFile},${files.moduleFile}`
);
expect(classifiers).toEqual(
'javadoc,sources,cinterop-Sentry.NSException,cinterop-Sentry,metadata,'
);
expect(types).toEqual('jar,jar,klib,klib,jar,module');
});

test('transform root target side artifacts', async () => {
(withTempDir as jest.MockedFunction<typeof withTempDir>).mockImplementation(
async cb => {
return await cb(tmpDirName);
}
);

const mvnTarget = createMavenTarget(getFullTargetConfig());
const files: Record<string, string | string[]> = {
javadocFile: `${tmpDirName}-javadoc.jar`,
sourcesFile: `${tmpDirName}-sources.jar`,
klibFiles: [],
allFile: `${tmpDirName}-all.jar`,
metadataFile: '',
moduleFile: `${tmpDirName}.module`,
kotlinToolingMetadataFile: `${tmpDirName}-kotlin-tooling-metadata.json`,
};
const {
sideArtifacts,
classifiers,
types,
} = mvnTarget.transformKmpSideArtifacts(true, false, files);
expect(sideArtifacts).toEqual(
`${files.javadocFile},${files.sourcesFile},${files.allFile},${files.kotlinToolingMetadataFile},${files.moduleFile}`
);
expect(classifiers).toEqual('javadoc,sources,all,kotlin-tooling-metadata,');
expect(types).toEqual('jar,jar,jar,json,module');
});
});

describe('upload', () => {
const tmpDirName = 'tmpDir';

Expand Down
Loading

0 comments on commit 8f84181

Please sign in to comment.