diff --git a/jest.config.js b/jest.config.js index 81fd8f5c8739420aa76d82a476210c7c1ecbab85..ff50dede20cdc9fc9bd802569ba8d8918ae89251 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: [ 'lib/**/*.{js,ts}', '!lib/**/*.{d,spec}.ts', - '!lib/**/{__fixtures__,__mocks__}/**/*.{js,ts}', + '!lib/**/{__fixtures__,__mocks__,__testutil__}/**/*.{js,ts}', ], coverageReporters: ci ? ['html', 'json', 'text-summary'] diff --git a/lib/manager/gradle/__snapshots__/index.spec.ts.snap b/lib/manager/gradle/__snapshots__/index.spec.ts.snap index 906a110e139826404f0725aabe2c89ded8133cbb..68432b5d63d7ada7de63459ce2d7ddb397d8faeb 100644 --- a/lib/manager/gradle/__snapshots__/index.spec.ts.snap +++ b/lib/manager/gradle/__snapshots__/index.spec.ts.snap @@ -16,7 +16,29 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, + }, + }, +] +`; + +exports[`manager/gradle extractPackageFile should execute gradle if gradlew is not available 1`] = ` +Array [ + Object { + "cmd": "gradle --init-script renovate-plugin.gradle renovate", + "options": Object { + "cwd": "localDir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "timeout": 60000, }, }, ] @@ -38,7 +60,29 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, + }, + }, +] +`; + +exports[`manager/gradle extractPackageFile should execute gradlew.bat when available on Windows 1`] = ` +Array [ + Object { + "cmd": "gradlew.bat --init-script renovate-plugin.gradle renovate", + "options": Object { + "cwd": "localDir", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "timeout": 60000, }, }, ] @@ -60,7 +104,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -82,7 +126,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -91,7 +135,7 @@ Array [ exports[`manager/gradle extractPackageFile should return empty if there is no dependency report 1`] = ` Array [ Object { - "cmd": "gradle --init-script renovate-plugin.gradle renovate", + "cmd": "./gradlew --init-script renovate-plugin.gradle renovate", "options": Object { "cwd": "localDir", "encoding": "utf-8", @@ -104,7 +148,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -247,7 +291,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -332,7 +376,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -475,7 +519,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -483,10 +527,22 @@ Array [ exports[`manager/gradle extractPackageFile should return null and gradle should not be executed if no root build.gradle 1`] = `Array []`; -exports[`manager/gradle extractPackageFile should run gradlew through \`sh\` when available but not executable 1`] = ` +exports[`manager/gradle extractPackageFile should use docker even if gradlew is available 1`] = ` Array [ Object { - "cmd": "sh gradlew --init-script renovate-plugin.gradle renovate", + "cmd": "docker pull renovate/gradle", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker ps --filter name=renovate_gradle -aq | xargs --no-run-if-empty docker rm -f", + "options": Object { + "encoding": "utf-8", + }, + }, + Object { + "cmd": "docker run --rm --name=renovate_gradle --label=renovate_child -v \\"localDir\\":\\"localDir\\" -w \\"localDir\\" renovate/gradle bash -l -c \\"./gradlew --init-script renovate-plugin.gradle renovate\\"", "options": Object { "cwd": "localDir", "encoding": "utf-8", @@ -499,14 +555,20 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 900000, }, }, ] `; -exports[`manager/gradle extractPackageFile should use docker even if gradlew is available 1`] = ` +exports[`manager/gradle extractPackageFile should use docker even if gradlew.bat is available on Windows 1`] = ` Array [ + Object { + "cmd": "docker pull renovate/gradle", + "options": Object { + "encoding": "utf-8", + }, + }, Object { "cmd": "docker ps --filter name=renovate_gradle -aq | xargs --no-run-if-empty docker rm -f", "options": Object { @@ -561,7 +623,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] @@ -623,7 +685,7 @@ Array [ "NO_PROXY": "localhost", "PATH": "/tmp/path", }, - "timeout": 20000, + "timeout": 60000, }, }, ] diff --git a/lib/manager/gradle/__testutil__/gradle.ts b/lib/manager/gradle/__testutil__/gradle.ts new file mode 100644 index 0000000000000000000000000000000000000000..6fc104c33102eb981896a71a25ce0d4923f5146b --- /dev/null +++ b/lib/manager/gradle/__testutil__/gradle.ts @@ -0,0 +1,87 @@ +import { spawnSync, SpawnSyncReturns } from 'child_process'; + +const failIfNoJavaEnv = 'CI'; + +const gradleJavaVersionSupport = { + 5: { min: 8, max: 12 }, + 6: { min: 8, max: 13 }, +}; + +const skipJava = process.env.SKIP_JAVA_TESTS === 'true'; +const enforceJava = process.env[failIfNoJavaEnv] === 'true' && !skipJava; + +function parseJavaVersion(javaVersionOutput: string): number { + const versionMatch = /version "(?:1\.)?(\d+)[\d._-]*"/.exec( + javaVersionOutput + ); + if (versionMatch !== null && versionMatch.length === 2) { + return parseInt(versionMatch[1], 10); + } + if (enforceJava) { + throw Error(`This test suite needs Java and ${failIfNoJavaEnv} is set. However, we cannot parse the Java version. +The output of java -version was: +${javaVersionOutput}`); + } + return 0; +} + +let cachedJavaVersion: number | null = null; + +function determineJavaVersion(): number { + if (cachedJavaVersion == null) { + let javaVersionCommand: SpawnSyncReturns<string>; + let error: Error; + try { + javaVersionCommand = spawnSync('java', ['-version'], { + encoding: 'utf8', + windowsHide: true, + }); + } catch (e) { + error = e; + } + if (javaVersionCommand.error) { + error = javaVersionCommand.error; + } + if (error) { + if (!enforceJava) { + return 0; + } + throw Error( + `This test suite needs Java and ${failIfNoJavaEnv} is set. +Result of java -version: +${error}` + ); + } + cachedJavaVersion = parseJavaVersion(javaVersionCommand.stderr); + } + return cachedJavaVersion; +} + +class WithGradle { + private gradleSupportsThisJavaVersion: boolean; + + constructor(private gradleVersion: number) { + const javaVersion = determineJavaVersion(); + if (gradleJavaVersionSupport[gradleVersion] === undefined) { + throw Error(`Unknown gradle version '${gradleVersion}'!`); + } + + const supportedJavaVersions = gradleJavaVersionSupport[gradleVersion]; + this.gradleSupportsThisJavaVersion = + javaVersion >= supportedJavaVersions.min && + javaVersion <= supportedJavaVersions.max; + if (!this.gradleSupportsThisJavaVersion && enforceJava) { + throw Error( + `This test needs a Java version between ${supportedJavaVersions.min} and ${supportedJavaVersions.max}. The current Java version is ${javaVersion} and ${failIfNoJavaEnv} is set!` + ); + } + } + + get it(): jest.It { + return !this.gradleSupportsThisJavaVersion || skipJava ? it.skip : it; + } +} + +export function ifSystemSupportsGradle(gradleVersion: number): WithGradle { + return new WithGradle(gradleVersion); +} diff --git a/lib/manager/gradle/gradle-updates-report.spec.ts b/lib/manager/gradle/gradle-updates-report.spec.ts index cb2ae6471edac2861687bd76c8613b81b5322447..0c46c1399cf90300b0133ab8474fa257113f7fa7 100644 --- a/lib/manager/gradle/gradle-updates-report.spec.ts +++ b/lib/manager/gradle/gradle-updates-report.spec.ts @@ -1,70 +1,18 @@ import tmp, { DirectoryResult } from 'tmp-promise'; import * as fs from 'fs-extra'; import * as path from 'path'; -import { spawnSync, SpawnSyncReturns } from 'child_process'; import { exec } from '../../util/exec'; import { GRADLE_DEPENDENCY_REPORT_OPTIONS } from './index'; import { createRenovateGradlePlugin, GRADLE_DEPENDENCY_REPORT_FILENAME, } from './gradle-updates-report'; +import { ifSystemSupportsGradle } from './__testutil__/gradle'; const fixtures = 'lib/manager/gradle/__fixtures__'; -const failIfNoJavaEnv = 'CI'; - -const gradleJavaVersionSupport = { - 5: { min: 8, max: 12 }, - 6: { min: 8, max: 13 }, -}; - -const skipJava = process.env.SKIP_JAVA_TESTS === 'true'; -const enforceJava = process.env[failIfNoJavaEnv] === 'true' && !skipJava; - -function parseJavaVersion(javaVersionOutput: string) { - const versionMatch = /version "(?:1\.)?(\d+)[\d._-]*"/.exec( - javaVersionOutput - ); - if (versionMatch !== null && versionMatch.length === 2) { - return parseInt(versionMatch[1], 10); - } - if (enforceJava) { - throw Error(`This test suite needs Java and ${failIfNoJavaEnv} is set. However, we cannot parse the Java version. -The output of java -version was: -${javaVersionOutput}`); - } - return 0; -} - -function determineJavaVersion(): number { - let javaVersionCommand: SpawnSyncReturns<string>; - let error: Error; - try { - javaVersionCommand = spawnSync('java', ['-version'], { - encoding: 'utf8', - windowsHide: true, - }); - } catch (e) { - error = e; - } - if (javaVersionCommand.error) { - error = javaVersionCommand.error; - } - if (error) { - if (!enforceJava) { - return 0; - } - throw Error( - `This test suite needs Java and ${failIfNoJavaEnv} is set. -Result of java -version: -${error}` - ); - } - return parseJavaVersion(javaVersionCommand.stderr); -} describe('lib/manager/gradle/gradle-updates-report', () => { let workingDir: DirectoryResult; - const javaVersion = determineJavaVersion(); beforeEach(async () => { workingDir = await tmp.dir({ unsafeCleanup: true }); @@ -72,16 +20,7 @@ describe('lib/manager/gradle/gradle-updates-report', () => { describe('createRenovateGradlePlugin', () => { for (const gradleVersion of [5, 6]) { - const supportedJavaVersions = gradleJavaVersionSupport[gradleVersion]; - const gradleSupportsThisJavaVersion = - javaVersion >= supportedJavaVersions.min && - javaVersion <= supportedJavaVersions.max; - if (!gradleSupportsThisJavaVersion && enforceJava) { - throw Error( - `This test needs a Java version between ${supportedJavaVersions.min} and ${supportedJavaVersions.max}. The current Java version is ${javaVersion} and ${failIfNoJavaEnv} is set!` - ); - } - (!gradleSupportsThisJavaVersion || skipJava ? it.skip : it)( + ifSystemSupportsGradle(gradleVersion).it( `generates a report for Gradle version ${gradleVersion}`, // the function creation is correct and intended // eslint-disable-next-line no-loop-func diff --git a/lib/manager/gradle/index.spec.ts b/lib/manager/gradle/index.spec.ts index 79a39ef379d9c9b51c298e99e7a411e6d1edb8c7..917f9b8f84647d2b5a17cc5803825d54674a69fc 100644 --- a/lib/manager/gradle/index.spec.ts +++ b/lib/manager/gradle/index.spec.ts @@ -1,28 +1,26 @@ import { toUnix } from 'upath'; import _fs from 'fs-extra'; -import fsReal from 'fs'; +import fsReal, { Stats } from 'fs'; import { exec as _exec } from 'child_process'; -import * as manager from '.'; -import { platform as _platform, Platform } from '../../platform'; +import * as _os from 'os'; +import tmp, { DirectoryResult } from 'tmp-promise'; +import * as path from 'path'; +import { platform as _platform } from '../../platform'; import { envMock, mockExecAll } from '../../../test/execUtil'; import * as _env from '../../util/exec/env'; -import { mocked } from '../../../test/util'; import { BinarySource } from '../../util/exec/common'; -import { setUtilConfig } from '../../util'; +import * as _docker from '../../util/exec/docker'; +import * as _util from '../../util'; +import { ifSystemSupportsGradle } from './__testutil__/gradle'; +import * as _manager from '.'; +import { ExtractConfig } from '../common'; -jest.mock('fs-extra'); -jest.mock('child_process'); -jest.mock('../../util/exec/env'); - -const platform: jest.Mocked<Platform> = _platform as any; -const fs: jest.Mocked<typeof _fs> = _fs as any; -const exec: jest.Mock<typeof _exec> = _exec as any; -const env = mocked(_env); +const fixtures = 'lib/manager/gradle/__fixtures__'; const config = { localDir: 'localDir', gradle: { - timeout: 20, + timeout: 60, }, }; @@ -36,22 +34,64 @@ const gradleOutput = { stderr: '', }; -describe('manager/gradle', () => { - beforeEach(() => { - jest.resetAllMocks(); - jest.resetModules(); - - fs.readFile.mockResolvedValue(updatesDependenciesReport as any); - fs.mkdir.mockResolvedValue(); - fs.exists.mockResolvedValue(true); - fs.access.mockResolvedValue(undefined); - platform.getFile.mockResolvedValue('some content'); - - env.getChildProcessEnv.mockReturnValue(envMock.basic); - setUtilConfig(config); - }); +function resetMocks() { + jest.resetAllMocks(); + jest.resetModules(); +} + +async function setupMocks() { + resetMocks(); + + jest.mock('fs-extra'); + jest.mock('child_process'); + jest.mock('../../util/exec/env'); + jest.mock('../../platform'); + jest.mock('os'); + const fs: jest.Mocked<typeof _fs> = require('fs-extra'); + const os: jest.Mocked<typeof _os> = require('os'); + const platform: jest.Mocked<typeof _platform> = require('../../platform') + .platform; + const env: jest.Mocked<typeof _env> = require('../../util/exec/env'); + const exec: jest.Mock<typeof _exec> = require('child_process').exec; + const util: jest.Mocked<typeof _util> = require('../../util'); + + platform.getFile.mockResolvedValue('some content'); + env.getChildProcessEnv.mockReturnValue(envMock.basic); + await util.setUtilConfig(config); + + return [require('.'), exec, fs, util, os]; +} + +describe('manager/gradle', () => { describe('extractPackageFile', () => { + let manager: typeof _manager; + let exec: jest.Mock<typeof _exec>; + let fs: jest.Mocked<typeof _fs>; + let util: jest.Mocked<typeof _util>; + let os: jest.Mocked<typeof _os>; + let docker: typeof _docker; + + beforeAll(async () => { + [manager, exec, fs, util, os] = await setupMocks(); + docker = require('../../util/exec/docker'); + }); + + afterAll(resetMocks); + + beforeEach(() => { + fs.readFile.mockResolvedValue(updatesDependenciesReport as any); + fs.mkdir.mockResolvedValue(); + fs.exists.mockResolvedValue(true); + fs.stat.mockResolvedValue({ + mode: 0o755, + isFile: () => true, + } as Stats); + exec.mockReset(); + docker.resetPrefetchedImages(); + os.platform.mockReturnValue('linux'); + }); + it('should return gradle dependencies', async () => { const execSnapshots = mockExecAll(exec, gradleOutput); @@ -141,10 +181,18 @@ describe('manager/gradle', () => { expect(execSnapshots).toMatchSnapshot(); }); - it('should run gradlew through `sh` when available but not executable', async () => { + it('should execute gradlew.bat when available on Windows', async () => { + const execSnapshots = mockExecAll(exec, gradleOutput); + os.platform.mockReturnValue('win32'); + + await manager.extractAllPackageFiles(config, ['build.gradle']); + expect(execSnapshots).toMatchSnapshot(); + }); + + it('should execute gradle if gradlew is not available', async () => { const execSnapshots = mockExecAll(exec, gradleOutput); - fs.access.mockRejectedValue(undefined); + fs.stat.mockResolvedValue({ isFile: () => false } as Stats); await manager.extractAllPackageFiles(config, ['build.gradle']); expect(execSnapshots).toMatchSnapshot(); }); @@ -152,7 +200,7 @@ describe('manager/gradle', () => { it('should return null and gradle should not be executed if no root build.gradle', async () => { const execSnapshots = mockExecAll(exec, gradleOutput); - fs.exists.mockResolvedValue(false); + fs.stat.mockResolvedValue({ isFile: () => false } as Stats); const packageFiles = ['foo/build.gradle']; expect( @@ -185,7 +233,7 @@ describe('manager/gradle', () => { }); it('should use docker if required', async () => { - setUtilConfig({ ...config, binarySource: BinarySource.Docker }); + util.setUtilConfig({ ...config, binarySource: BinarySource.Docker }); const execSnapshots = mockExecAll(exec, gradleOutput); const configWithDocker = { @@ -198,7 +246,21 @@ describe('manager/gradle', () => { }); it('should use docker even if gradlew is available', async () => { - setUtilConfig({ ...config, binarySource: BinarySource.Docker }); + util.setUtilConfig({ ...config, binarySource: BinarySource.Docker }); + const execSnapshots = mockExecAll(exec, gradleOutput); + + const configWithDocker = { + binarySource: BinarySource.Docker, + ...config, + gradle: {}, + }; + await manager.extractAllPackageFiles(configWithDocker, ['build.gradle']); + + expect(execSnapshots).toMatchSnapshot(); + }); + + it('should use docker even if gradlew.bat is available on Windows', async () => { + os.platform.mockReturnValue('win32'); const execSnapshots = mockExecAll(exec, gradleOutput); const configWithDocker = { @@ -213,6 +275,14 @@ describe('manager/gradle', () => { }); describe('updateDependency', () => { + let manager: typeof _manager; + let exec: jest.Mock<typeof _exec>; + + beforeAll(async () => { + [manager, exec] = await setupMocks(); + }); + afterAll(resetMocks); + it('should update an existing module dependency', () => { const execSnapshots = mockExecAll(exec, gradleOutput); @@ -303,4 +373,70 @@ describe('manager/gradle', () => { expect(execSnapshots).toMatchSnapshot(); }); }); + + describe('executeGradle integration', () => { + const SUCCESS_FILE = 'success.indicator'; + let workingDir: DirectoryResult; + let testRunConfig: ExtractConfig; + + const manager = require('.'); + + beforeEach(async () => { + workingDir = await tmp.dir({ unsafeCleanup: true }); + testRunConfig = { ...config, localDir: workingDir.path }; + await _fs.copy(`${fixtures}/minimal-project`, workingDir.path); + await _fs.copy(`${fixtures}/gradle-wrappers/6`, workingDir.path); + + const mockPluginContent = ` +allprojects { + tasks.register("renovate") { + doLast { + new File('${SUCCESS_FILE}').write 'success' + } + } +}`; + await _fs.writeFile( + path.join(workingDir.path, 'renovate-plugin.gradle'), + mockPluginContent + ); + }); + + ifSystemSupportsGradle(6).it( + 'executes an executable gradle wrapper', + async () => { + const gradlew = await fsReal.promises.stat( + path.join(workingDir.path, 'gradlew') + ); + await manager.executeGradle(testRunConfig, workingDir.path, gradlew); + await expect( + fsReal.promises.readFile( + path.join(workingDir.path, SUCCESS_FILE), + 'utf8' + ) + ).resolves.toBe('success'); + }, + 120000 + ); + + ifSystemSupportsGradle(6).it( + 'executes a not-executable gradle wrapper', + async () => { + await fsReal.promises.chmod( + path.join(workingDir.path, 'gradlew'), + '444' + ); + const gradlew = await fsReal.promises.stat( + path.join(workingDir.path, 'gradlew') + ); + await manager.executeGradle(testRunConfig, workingDir.path, gradlew); + await expect( + fsReal.promises.readFile( + path.join(workingDir.path, SUCCESS_FILE), + 'utf8' + ) + ).resolves.toBe('success'); + }, + 120000 + ); + }); }); diff --git a/lib/manager/gradle/index.ts b/lib/manager/gradle/index.ts index 393d71744999ba2d479ab4949c99a8a905649b4f..7123933b766cd156153e34aec229bf48538d6625 100644 --- a/lib/manager/gradle/index.ts +++ b/lib/manager/gradle/index.ts @@ -1,20 +1,10 @@ -import { access, constants, exists } from 'fs-extra'; +import * as os from 'os'; +import * as fs from 'fs-extra'; +import { Stats } from 'fs'; import upath from 'upath'; - import { exec, ExecOptions } from '../../util/exec'; import { logger } from '../../logger'; import * as mavenVersioning from '../../versioning/maven'; - -import { - init, - collectVersionVariables, - updateGradleVersion, - GradleDependency, -} from './build-gradle'; -import { - createRenovateGradlePlugin, - extractDependenciesFromUpdatesReport, -} from './gradle-updates-report'; import { PackageFile, ExtractConfig, @@ -25,42 +15,59 @@ import { platform } from '../../platform'; import { LANGUAGE_JAVA } from '../../constants/languages'; import * as datasourceMaven from '../../datasource/maven'; import { DatasourceError } from '../../datasource'; +import { BinarySource } from '../../util/exec/common'; +import { + init, + collectVersionVariables, + updateGradleVersion, + GradleDependency, +} from './build-gradle'; +import { + createRenovateGradlePlugin, + extractDependenciesFromUpdatesReport, +} from './gradle-updates-report'; export const GRADLE_DEPENDENCY_REPORT_OPTIONS = '--init-script renovate-plugin.gradle renovate'; const TIMEOUT_CODE = 143; -async function canExecute(path: string): Promise<boolean> { - try { - await access(path, constants.X_OK); - return true; - } catch { - return false; +function gradleWrapperFileName(config: ExtractConfig): string { + if ( + os.platform() === 'win32' && + config.binarySource !== BinarySource.Docker + ) { + return 'gradlew.bat'; } + return './gradlew'; } -async function getGradleCommandLine( +async function prepareGradleCommandLine( config: ExtractConfig, - cwd: string + cwd: string, + gradlew: Stats ): Promise<string> { const args = GRADLE_DEPENDENCY_REPORT_OPTIONS; + const gradlewName = gradleWrapperFileName(config); + + if (gradlew.isFile()) { + // if the file is not executable by others + // eslint-disable-next-line no-bitwise + if ((gradlew.mode & 0o1) === 0) { + // add the execution permission to the owner, group and others + // eslint-disable-next-line no-bitwise + await fs.chmod(upath.join(cwd, gradlewName), gradlew.mode | 0o111); + } - const gradlewPath = upath.join(cwd, 'gradlew'); - const gradlewExists = await exists(gradlewPath); - const gradlewExecutable = gradlewExists && (await canExecute(gradlewPath)); - if (gradlewExecutable) { - return `./gradlew ${args}`; - } - if (gradlewExists) { - return `sh gradlew ${args}`; + return `${gradlewName} ${args}`; } return `gradle ${args}`; } -async function executeGradle( +export async function executeGradle( config: ExtractConfig, - cwd: string + cwd: string, + gradlew: Stats ): Promise<void> { let stdout: string; let stderr: string; @@ -68,7 +75,7 @@ async function executeGradle( config.gradle && config.gradle.timeout ? config.gradle.timeout * 1000 : undefined; - const cmd = await getGradleCommandLine(config, cwd); + const cmd = await prepareGradleCommandLine(config, cwd, gradlew); const execOptions: ExecOptions = { timeout, cwd, @@ -97,19 +104,19 @@ export async function extractAllPackageFiles( packageFiles: string[] ): Promise<PackageFile[] | null> { let rootBuildGradle: string | undefined; + let gradlew: Stats; for (const packageFile of packageFiles) { + const dirname = upath.dirname(packageFile); + const gradlewPath = upath.join(dirname, gradleWrapperFileName(config)); + gradlew = await fs.stat(upath.join(config.localDir, gradlewPath)); + if (['build.gradle', 'build.gradle.kts'].includes(packageFile)) { rootBuildGradle = packageFile; break; } // If there is gradlew in the same directory, the directory should be a Gradle project root - const dirname = upath.dirname(packageFile); - const gradlewPath = upath.join(dirname, 'gradlew'); - const gradlewExists = await exists( - upath.join(config.localDir, gradlewPath) - ); - if (gradlewExists) { + if (gradlew.isFile()) { rootBuildGradle = packageFile; break; } @@ -123,7 +130,7 @@ export async function extractAllPackageFiles( const cwd = upath.join(config.localDir, upath.dirname(rootBuildGradle)); await createRenovateGradlePlugin(cwd); - await executeGradle(config, cwd); + await executeGradle(config, cwd, gradlew); init(); diff --git a/tsconfig.app.json b/tsconfig.app.json index 9802bf51a91005edd903da718fac2254ce398159..5fe1b487cf013b4298af84341a51bae7bbcbce80 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -14,6 +14,7 @@ "./dist", "**/__mocks__/**", "**/__fixtures__/**", + "**/__testutil__/**", "**/*.spec.ts", "./test" ]