diff --git a/lib/compiler-finder.js b/lib/compiler-finder.ts similarity index 87% rename from lib/compiler-finder.js rename to lib/compiler-finder.ts index 48f62d81de752e73a4c9bc86e82d1f31db94aa4e..b4f068b4264905255736a5adc04caa3f8d388e25 100644 --- a/lib/compiler-finder.js +++ b/lib/compiler-finder.ts @@ -33,21 +33,48 @@ import urljoin from 'url-join'; import {InstanceFetcher} from './aws'; import {logger} from './logger'; +import {PropertyGetter, PropertyValue, Widen} from './properties.interfaces'; +import {CompilerProps, RawPropertiesGetter} from './properties'; +import AWS from 'aws-sdk'; +import {ClientOptionsHandler} from './options-handler'; +import {CompileHandler} from './handlers/compile'; const sleep = promisify(setTimeout); +export type CompilerFinderArguments = { + rootDir: string; + env: string[]; + hostname: string[]; + port: number; + gitReleaseName: string; + releaseBuildNumber: string; + wantedLanguages: string | null; + doCache: boolean; + fetchCompilersFromRemote: boolean; + ensureNoCompilerClash: boolean; + suppressConsoleLog: boolean; +}; + /*** * Finds and initializes the compilers stored on the properties files */ export class CompilerFinder { - /*** - * @param {CompileHandler} compileHandler - * @param {CompilerProps} compilerProps - * @param {propsFor} awsProps - * @param {Object} args - * @param {Object} optionsHandler - */ - constructor(compileHandler, compilerProps, awsProps, args, optionsHandler) { + compilerProps: CompilerProps['get']; + ceProps: PropertyGetter; + awsProps: PropertyGetter; + args: CompilerFinderArguments; + compileHandler: CompileHandler; + languages: Record<string, any>; + awsPoller: InstanceFetcher | null = null; + optionsHandler: ClientOptionsHandler; + + constructor( + compileHandler: CompileHandler, + compilerProps: CompilerProps, + awsProps: PropertyGetter, + args: CompilerFinderArguments, + optionsHandler: ClientOptionsHandler, + ) { this.compilerProps = compilerProps.get.bind(compilerProps); this.ceProps = compilerProps.ceProps; this.awsProps = awsProps; @@ -95,7 +122,7 @@ export class CompilerFinder { `${uriSchema}://${host}:${port}${apiPath}\n` + `Status Code: ${statusCode}`, ); - } else if (!/^application\/json/.test(contentType)) { + } else if (!contentType || !/^application\/json/.test(contentType)) { error = new Error( 'Invalid content-type.\n' + `Expected application/json but received ${contentType}`, @@ -129,7 +156,7 @@ export class CompilerFinder { return compiler; }); resolve(compilers); - } catch (e) { + } catch (e: any) { logger.error(`Error parsing response from ${uri} '${str}': ${e.message}`); reject(e); } @@ -154,7 +181,7 @@ export class CompilerFinder { logger.info('Fetching instances from AWS'); const instances = await this.awsInstances(); return Promise.all( - instances.map(instance => { + (instances.filter(instance => instance !== undefined) as AWS.EC2.Instance[]).map(instance => { logger.info('Checking instance ' + instance.InstanceId); const address = this.awsProps('externalTestMode', false) ? instance.PublicDnsName @@ -164,13 +191,16 @@ export class CompilerFinder { ); } - async compilerConfigFor(langId, compilerId, parentProps) { + async compilerConfigFor(langId: string, compilerId: string, parentProps: RawPropertiesGetter) { const base = `compiler.${compilerId}.`; - function props(propName, def) { + function props(propName: string, defaultValue: undefined): PropertyValue; + function props<T extends PropertyValue>(propName: string, defaultValue: Widen<T>): typeof defaultValue; + function props<T extends PropertyValue>(propName: string, defaultValue?: unknown): T; + function props(propName: string, defaultValue?: unknown) { const propsForCompiler = parentProps(langId, base + propName); if (propsForCompiler !== undefined) return propsForCompiler; - return parentProps(langId, propName, def); + return parentProps(langId, propName, defaultValue); } const ceToolsPath = props('ceToolsPath', './'); @@ -206,7 +236,7 @@ export class CompilerFinder { if (envVarsString === '') { return []; } - const arr = []; + const arr: [string, string][] = []; for (const el of envVarsString.split(':')) { const [env, setting] = el.split('='); arr.push([env, setting]); @@ -261,7 +291,7 @@ export class CompilerFinder { notification: props('notification', ''), isSemVer: isSemVer, semver: semverVer, - libsArr: this.getSupportedLibrariesArr(props, langId), + libsArr: this.getSupportedLibrariesArr(props), tools: _.omit(this.optionsHandler.get().tools[langId], tool => tool.isCompilerExcluded(compilerId, props)), unwiseOptions: props('unwiseOptions', '').split('|'), hidden: props('hidden', false), @@ -319,7 +349,10 @@ export class CompilerFinder { } return this.compilerProps(langId, `group.${groupName}.${name}`, parentProps(langId, name, def)); }; - const exes = _.compact(this.compilerProps(langId, `group.${groupName}.compilers`, '').split(':')); + // TODO: Eliminate this cast + const exes = _.compact( + (this.compilerProps(langId, `group.${groupName}.compilers`, '') as string).split(':'), + ); logger.debug(`Processing compilers from group ${groupName}`); return Promise.all(exes.map(compiler => this.recurseGetCompilers(langId, compiler, props))); } @@ -328,15 +361,15 @@ export class CompilerFinder { } async getCompilers() { - const compilers = []; + const compilers: any[] = []; _.each(this.getExes(), (exs, langId) => { _.each(exs, exe => compilers.push(this.recurseGetCompilers(langId, exe, this.compilerProps))); }); return Promise.all(compilers); } - ensureDistinct(compilers) { - const ids = {}; + ensureDistinct(compilers: any[]) { + const ids: Record<string, any> = {}; let foundClash = false; _.each(compilers, compiler => { if (!ids[compiler.id]) ids[compiler.id] = []; @@ -372,14 +405,20 @@ export class CompilerFinder { } getExes() { - const langToCompilers = this.compilerProps(this.languages, 'compilers', '', exs => _.compact(exs.split(':'))); + // TODO: Fix typing here + const langToCompilers = this.compilerProps( + this.languages, + 'compilers', + '', + exs => _.compact((exs as string).split(':')) as unknown as PropertyValue, + ) as _.Dictionary<any>; this.addNdkExes(langToCompilers); logger.info('Exes found:', langToCompilers); return langToCompilers; } addNdkExes(langToCompilers) { - const ndkPaths = this.compilerProps(this.languages, 'androidNdk'); + const ndkPaths = this.compilerProps(this.languages, 'androidNdk') as _.Dictionary<any>; _.each(ndkPaths, (ndkPath, langId) => { if (ndkPath) { const toolchains = fs.readdirSync(`${ndkPath}/toolchains`); diff --git a/lib/properties.ts b/lib/properties.ts index 3075e2cdd4a4ec446844f2c255f98b4a731f1f85..d019f4556708a4c62a0529b78897cd3e68d56ea4 100644 --- a/lib/properties.ts +++ b/lib/properties.ts @@ -67,6 +67,8 @@ export function get(base: string, property: string, defaultValue?: unknown): unk return result; } +export type RawPropertiesGetter = typeof get; + export function parseProperties(blob, name) { const props = {}; for (const [index, lineOrig] of blob.split('\n').entries()) { @@ -132,9 +134,9 @@ type LanguageDef = { * Compiler property fetcher */ export class CompilerProps { - private languages: Record<string, any>; - private propsByLangId: Record<string, PropertyGetter>; - private ceProps: any; + public readonly languages: Record<string, any>; + public readonly propsByLangId: Record<string, PropertyGetter>; + public readonly ceProps: any; /*** * Creates a CompilerProps lookup function @@ -171,9 +173,9 @@ export class CompilerProps { * @returns {*} Transformed value(s) found or fn(defaultValue) */ get( - langs: string | LanguageDef[], + langs: string | LanguageDef[] | Record<string, any>, key: string, - defaultValue: PropertyValue, + defaultValue?: PropertyValue, fn: (item: PropertyValue, language?: any) => PropertyValue = _.identity, ) { fn = fn || _.identity;