Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,7 @@ targets:
kmp:
rootDistDirRegex: /sentry-kotlin-multiplatform-[0-9]+.*$/
appleDistDirRegex: /sentry-kotlin-multiplatform-(macos|ios|tvos|watchos).*/
klibDistDirRegex: /sentry-kotlin-multiplatform-(js|wasm-js).*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also open for another name for this config

```

### Symbol Collector (`symbol-collector`)
Expand Down
112 changes: 106 additions & 6 deletions src/targets/__tests__/maven.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { retrySpawnProcess, sleep } from '../../utils/async';
import { withTempDir } from '../../utils/files';
import { importGPGKey } from '../../utils/gpg';
import * as fs from 'fs';

jest.mock('../../utils/files');
jest.mock('../../utils/gpg');
Expand Down Expand Up @@ -73,9 +74,10 @@ function getFullTargetConfig(): any {
fileReplacerStr: 'replacer',
},
kmp: {
rootDistDirRegex: '/distDir/',
rootDistDirRegex: '/root-distDir/',
appleDistDirRegex: '/apple-distDir/',
},
klibDistDirRegex: '/klib-distDir/',
},
};
}

Expand Down Expand Up @@ -258,6 +260,7 @@ describe('Maven target configuration', () => {
expect(typeof mvnTarget.config.android.fileReplacerStr).toBe('string');
expect(typeof mvnTarget.config.kmp.rootDistDirRegex).toBe('string');
expect(typeof mvnTarget.config.kmp.appleDistDirRegex).toBe('string');
expect(typeof mvnTarget.config.kmp.klibDistDirRegex).toBe('string');
});

test('import GPG private key if one is present in the environment', async () => {
Expand Down Expand Up @@ -294,6 +297,38 @@ describe('publish', () => {
describe('transform KMP artifacts', () => {
const tmpDirName = 'tmpDir';

test('transform klib distDir 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}.klib`,
],
allFile: '',
metadataFile: ``,
moduleFile: `${tmpDirName}.module`,
};
const {
sideArtifacts,
classifiers,
types,
} = mvnTarget.transformKmpSideArtifacts(false, false, true, files);
expect(sideArtifacts).toEqual(
`${files.javadocFile},${files.sourcesFile},${files.klibFiles},${files.moduleFile}`
);
expect(classifiers).toEqual(
'javadoc,sources,,'
);
expect(types).toEqual('jar,jar,klib,module');
});

test('transform apple target side artifacts', async () => {
(withTempDir as jest.MockedFunction<typeof withTempDir>).mockImplementation(
async cb => {
Expand All @@ -317,7 +352,7 @@ describe('transform KMP artifacts', () => {
sideArtifacts,
classifiers,
types,
} = mvnTarget.transformKmpSideArtifacts(false, true, files);
} = mvnTarget.transformKmpSideArtifacts(false, true, false, files);
expect(sideArtifacts).toEqual(
`${files.javadocFile},${files.sourcesFile},${files.klibFiles},${files.metadataFile},${files.moduleFile}`
);
Expand Down Expand Up @@ -348,7 +383,7 @@ describe('transform KMP artifacts', () => {
sideArtifacts,
classifiers,
types,
} = mvnTarget.transformKmpSideArtifacts(true, false, files);
} = mvnTarget.transformKmpSideArtifacts(true, false, false, files);
expect(sideArtifacts).toEqual(
`${files.javadocFile},${files.sourcesFile},${files.allFile},${files.kotlinToolingMetadataFile},${files.moduleFile}`
);
Expand Down Expand Up @@ -507,8 +542,6 @@ describe('upload', () => {
});

test('should skip upload for artifacts without any POM/BOM', async () => {
// simple mock to always use the same temporary directory,
// instead of creating a new one
(withTempDir as jest.MockedFunction<typeof withTempDir>).mockImplementation(
async cb => {
return await cb(tmpDirName);
Expand All @@ -531,6 +564,73 @@ describe('upload', () => {

expect(retrySpawnProcess).toHaveBeenCalledTimes(0);
});

test('upload KMP klib-only distribution', async () => {
const klibDistDirName = 'sentry-klib-distDir-linuxx64-1.0.0'; // matches klib regex
const klibDistDir = `${tmpDirName}/${klibDistDirName}`;

(withTempDir as jest.MockedFunction<typeof withTempDir>).mockImplementation(
async cb => {
return await cb(tmpDirName);
}
);

// Override fs.promises.readdir for this test to return klib files
const readdirSpy = jest.spyOn(fs.promises, 'readdir').mockImplementation((dirPath: any) => {
if (dirPath.toString().includes(klibDistDirName)) {
return Promise.resolve([
`${klibDistDirName}-javadoc.jar`,
`${klibDistDirName}.klib`,
`${klibDistDirName}-sources.jar`,
`${klibDistDirName}.module`,
POM_DEFAULT_FILENAME,
] as any);
}
return Promise.resolve([] as any);
});

const mvnTarget = createMavenTarget(getFullTargetConfig());
mvnTarget.getArtifactsForRevision = jest
.fn()
.mockResolvedValueOnce([{ filename: `${klibDistDirName}.zip` }]);
mvnTarget.artifactProvider.downloadArtifact = jest
.fn()
.mockResolvedValueOnce('artifact/download/path');
mvnTarget.isBomFile = jest.fn().mockResolvedValueOnce(false);
mvnTarget.getPomFileInDist = jest.fn().mockResolvedValueOnce('pom-default.xml');
mvnTarget.fileExists = jest.fn().mockResolvedValue(true);

await mvnTarget.upload('r3v1s10n');

expect(retrySpawnProcess).toHaveBeenCalledTimes(1);
const callArgs = (retrySpawnProcess as jest.MockedFunction<
typeof retrySpawnProcess
>).mock.calls[0];

expect(callArgs).toHaveLength(2);
expect(callArgs[0]).toEqual(DEFAULT_OPTION_VALUE);

const cmdArgs = callArgs[1] as string[];
expect(cmdArgs).toHaveLength(11);
expect(cmdArgs[0]).toBe('gpg:sign-and-deploy-file');
expect(cmdArgs[1]).toMatch(new RegExp(`-Dfile=${klibDistDir}/${klibDistDirName}`));
expect(cmdArgs[2]).toBe(
`-Dfiles=${klibDistDir}/${klibDistDirName}-javadoc.jar,${klibDistDir}/${klibDistDirName}-sources.jar,${klibDistDir}/${klibDistDirName}.klib,${klibDistDir}/${klibDistDirName}.module`
);
expect(cmdArgs[3]).toBe(`-Dclassifiers=javadoc,sources,,`);
expect(cmdArgs[4]).toBe(`-Dtypes=jar,jar,klib,module`);
expect(cmdArgs[5]).toMatch(
new RegExp(`-DpomFile=${klibDistDir}/pom-default\\.xml`)
);
expect(cmdArgs[6]).toBe(`-DrepositoryId=${DEFAULT_OPTION_VALUE}`);
expect(cmdArgs[7]).toBe(`-Durl=${DEFAULT_OPTION_VALUE}`);
expect(cmdArgs[8]).toBe(`-Dgpg.passphrase=${DEFAULT_OPTION_VALUE}`);
expect(cmdArgs[9]).toBe('--settings');
expect(cmdArgs[10]).toBe(DEFAULT_OPTION_VALUE);

// Restore original mock
readdirSpy.mockRestore();
});
});

describe('closeAndReleaseRepository', () => {
Expand Down
80 changes: 54 additions & 26 deletions src/targets/maven.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type KotlinMultiplatformFields = {
| {
appleDistDirRegex: RegExp;
rootDistDirRegex: RegExp;
klibDistDirRegex: RegExp;
};
};

Expand Down Expand Up @@ -170,10 +171,17 @@ export class MavenTarget extends BaseTarget {
);
}

if (!this.config.kmp.klibDistDirRegex) {
throw new ConfigurationError(
'Required klib configuration for Kotlin Multiplatform is incorrect. See the documentation for more details.'
);
}

return {
kmp: {
appleDistDirRegex: stringToRegexp(this.config.kmp.appleDistDirRegex),
rootDistDirRegex: stringToRegexp(this.config.kmp.rootDistDirRegex),
klibDistDirRegex: stringToRegexp(this.config.kmp.klibDistDirRegex),
},
};
}
Expand Down Expand Up @@ -376,13 +384,16 @@ export class MavenTarget extends BaseTarget {
const isAppleDistDir = this.mavenConfig.kmp.appleDistDirRegex.test(
moduleName
);
const isKlibDistDir = this.mavenConfig.kmp.klibDistDirRegex.test(
moduleName
);
const files = await this.getFilesForKmpMavenPomDist(distDir);
const { targetFile, pomFile } = files;
const {
sideArtifacts,
classifiers,
types,
} = this.transformKmpSideArtifacts(isRootDistDir, isAppleDistDir, files);
} = this.transformKmpSideArtifacts(isRootDistDir, isAppleDistDir, isKlibDistDir, files);

await retrySpawnProcess(this.mavenConfig.mavenCliPath, [
'gpg:sign-and-deploy-file',
Expand Down Expand Up @@ -418,6 +429,7 @@ export class MavenTarget extends BaseTarget {
*
* @param isRootDistDir boolean indicating whether the distDir is the root distDir
* @param isAppleDistDir boolean indicating whether the distDir is the Apple distDir
* @param isKlibDistDir boolean indicating whether the distDir is the klib-only distDir
* @param files an object containing the input files, as described above
* @returns a Record with three fields:
* - sideArtifacts: a comma-separated string listing the paths to all generated "side artifacts"
Expand All @@ -427,6 +439,7 @@ export class MavenTarget extends BaseTarget {
public transformKmpSideArtifacts(
isRootDistDir: boolean,
isAppleDistDir: boolean,
isKlibDistDir: boolean,
files: Record<string, string | string[]>
): Record<string, string | string[]> {
const {
Expand All @@ -447,25 +460,38 @@ export class MavenTarget extends BaseTarget {
types += ',jar,json';
classifiers += ',all,kotlin-tooling-metadata';
} else if (isAppleDistDir) {
if (klibFiles) {
sideArtifacts += `,${klibFiles}`;

// In order to upload cinterop klib files we need to extract the classifier from the file name.
// e.g: "sentry-kotlin-multiplatform-iosarm64-0.0.1-cinterop-Sentry.klib",
// the classifier is "cinterop-Sentry".
for (let i = 0; i < klibFiles.length; i++) {
const input = klibFiles[i];
const start = input.indexOf('cinterop');
const end = input.indexOf('.klib', start);
const classifier = input.substring(start, end);

types += ',klib';
classifiers += `,${classifier}`;
}
if (!Array.isArray(klibFiles)) {
throw new ConfigurationError(
'klib files in apple distributions must be an array'
);
}
sideArtifacts += `,${klibFiles}`;

// In order to upload cinterop klib files we need to extract the classifier from the file name.
// e.g: "sentry-kotlin-multiplatform-iosarm64-0.0.1-cinterop-Sentry.klib",
// the classifier is "cinterop-Sentry".
for (let i = 0; i < klibFiles.length; i++) {
const input = klibFiles[i];
const start = input.indexOf('cinterop');
const end = input.indexOf('.klib', start);
const classifier = input.substring(start, end);

types += ',klib';
classifiers += `,${classifier}`;
}

sideArtifacts += `,${metadataFile}`;
types += ',jar';
classifiers += ',metadata';
} else if (isKlibDistDir) {
if (!Array.isArray(klibFiles) || klibFiles.length !== 1) {
throw new ConfigurationError(
'klib files in klib-only distributions must be an array with exactly one element'
);
}
sideArtifacts += `,${klibFiles}`;
types += ',klib';
classifiers += ',';
}

// .module files should be available in every KMP artifact
Expand Down Expand Up @@ -596,12 +622,12 @@ export class MavenTarget extends BaseTarget {

const moduleName = parse(distDir).base;
if (this.mavenConfig.kmp !== false) {
const isRootDistDir = this.mavenConfig.kmp.rootDistDirRegex.test(
moduleName
);
const isAppleDistDir = this.mavenConfig.kmp.appleDistDirRegex.test(
moduleName
);
const { klibDistDirRegex, appleDistDirRegex, rootDistDirRegex } = this.mavenConfig.kmp;

const isRootDistDir = rootDistDirRegex.test(moduleName);
const isAppleDistDir = appleDistDirRegex.test(moduleName);
const isKlibDistDir = klibDistDirRegex.test(moduleName);

if (isRootDistDir) {
files['allFile'] = join(distDir, `${moduleName}-all.jar`);
files['kotlinToolingMetadataFile'] = join(
Expand All @@ -615,6 +641,8 @@ export class MavenTarget extends BaseTarget {
.map(file => join(distDir, file));

files['klibFiles'] = cinteropFiles;
} else if (isKlibDistDir) {
files['klibFiles'] = [join(distDir, `${moduleName}.klib`)];
}
}
return files;
Expand Down Expand Up @@ -649,13 +677,13 @@ export class MavenTarget extends BaseTarget {
}
}
if (this.mavenConfig.kmp !== false) {
const isAppleDistDir = this.mavenConfig.kmp.appleDistDirRegex.test(
moduleName
);
if (isAppleDistDir) {
const { klibDistDirRegex, appleDistDirRegex } = this.mavenConfig.kmp;

if (klibDistDirRegex.test(moduleName) || appleDistDirRegex.test(moduleName)) {
return `${moduleName}.klib`;
}
}

return `${moduleName}.jar`;
}

Expand Down
Loading