Files
unity-builder/src/model/cloud-runner/providers/provider-loader.ts
Frostebite 464a9d1265 feat: Add dynamic provider loader with improved error handling (#734)
* feat: Add dynamic provider loader with improved error handling

- Create provider-loader.ts with function-based dynamic import functionality
- Update CloudRunner.setupSelectedBuildPlatform to use dynamic loader for unknown providers
- Add comprehensive error handling for missing packages and interface validation
- Include test coverage for successful loading and error scenarios
- Maintain backward compatibility with existing built-in providers
- Add ProviderLoader class wrapper for backward compatibility
- Support both built-in providers (via switch) and external providers (via dynamic import)

* fix: Resolve linting errors in provider loader

- Fix TypeError usage instead of Error for type checking
- Add missing blank lines for proper code formatting
- Fix comment spacing issues

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* fix: Fix AWS job dependencies and remove duplicate localstack tests

- Update AWS job to depend on both k8s and localstack jobs
- Remove duplicate localstack tests from k8s job (now only runs k8s tests)
- Remove unused cloud-runner-localstack job from main integrity check
- Fix AWS SDK warnings by using Uint8Array(0) instead of empty string for S3 PutObject
- Rename localstack-and-k8s job to k8s job for clarity

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Fix provider-loader tests and URL parser consistency

- Fixed provider-loader test failures (constructor validation, module imports)
- Fixed provider-url-parser to return consistent base URLs for GitHub sources
- Updated error handling to use TypeError consistently
- All provider-loader and provider-url-parser tests now pass
- Fixed prettier and eslint formatting issues

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* m

* m
2025-09-13 00:54:21 +01:00

159 lines
5.4 KiB
TypeScript

import { ProviderInterface } from './provider-interface';
import BuildParameters from '../../build-parameters';
import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import { parseProviderSource, logProviderSource, ProviderSourceInfo } from './provider-url-parser';
import { ProviderGitManager } from './provider-git-manager';
// import path from 'path'; // Not currently used
/**
* Dynamically load a provider package by name, URL, or path.
* @param providerSource Provider source (name, URL, or path)
* @param buildParameters Build parameters passed to the provider constructor
* @throws Error when the provider cannot be loaded or does not implement ProviderInterface
*/
export default async function loadProvider(
providerSource: string,
buildParameters: BuildParameters,
): Promise<ProviderInterface> {
CloudRunnerLogger.log(`Loading provider: ${providerSource}`);
// Parse the provider source to determine its type
const sourceInfo = parseProviderSource(providerSource);
logProviderSource(providerSource, sourceInfo);
let modulePath: string;
let importedModule: any;
try {
// Handle different source types
switch (sourceInfo.type) {
case 'github': {
CloudRunnerLogger.log(`Processing GitHub repository: ${sourceInfo.owner}/${sourceInfo.repo}`);
// Ensure the repository is available locally
const localRepoPath = await ProviderGitManager.ensureRepositoryAvailable(sourceInfo);
// Get the path to the provider module within the repository
modulePath = ProviderGitManager.getProviderModulePath(sourceInfo, localRepoPath);
CloudRunnerLogger.log(`Loading provider from: ${modulePath}`);
break;
}
case 'local': {
modulePath = sourceInfo.path;
CloudRunnerLogger.log(`Loading provider from local path: ${modulePath}`);
break;
}
case 'npm': {
modulePath = sourceInfo.packageName;
CloudRunnerLogger.log(`Loading provider from NPM package: ${modulePath}`);
break;
}
default: {
// Fallback to built-in providers or direct import
const providerModuleMap: Record<string, string> = {
aws: './aws',
k8s: './k8s',
test: './test',
'local-docker': './docker',
'local-system': './local',
local: './local',
};
modulePath = providerModuleMap[providerSource] || providerSource;
CloudRunnerLogger.log(`Loading provider from module path: ${modulePath}`);
break;
}
}
// Import the module
importedModule = await import(modulePath);
} catch (error) {
throw new Error(`Failed to load provider package '${providerSource}': ${(error as Error).message}`);
}
// Extract the provider class/function
const Provider = importedModule.default || importedModule;
// Validate that we have a constructor
if (typeof Provider !== 'function') {
throw new TypeError(`Provider package '${providerSource}' does not export a constructor function`);
}
// Instantiate the provider
let instance: any;
try {
instance = new Provider(buildParameters);
} catch (error) {
throw new Error(`Failed to instantiate provider '${providerSource}': ${(error as Error).message}`);
}
// Validate that the instance implements the required interface
const requiredMethods = [
'cleanupWorkflow',
'setupWorkflow',
'runTaskInWorkflow',
'garbageCollect',
'listResources',
'listWorkflow',
'watchWorkflow',
];
for (const method of requiredMethods) {
if (typeof instance[method] !== 'function') {
throw new TypeError(
`Provider package '${providerSource}' does not implement ProviderInterface. Missing method '${method}'.`,
);
}
}
CloudRunnerLogger.log(`Successfully loaded provider: ${providerSource}`);
return instance as ProviderInterface;
}
/**
* ProviderLoader class for backward compatibility and additional utilities
*/
export class ProviderLoader {
/**
* Dynamically loads a provider by name, URL, or path (wrapper around loadProvider function)
* @param providerSource - The provider source (name, URL, or path) to load
* @param buildParameters - Build parameters to pass to the provider constructor
* @returns Promise<ProviderInterface> - The loaded provider instance
* @throws Error if provider package is missing or doesn't implement ProviderInterface
*/
static async loadProvider(providerSource: string, buildParameters: BuildParameters): Promise<ProviderInterface> {
return loadProvider(providerSource, buildParameters);
}
/**
* Gets a list of available provider names
* @returns string[] - Array of available provider names
*/
static getAvailableProviders(): string[] {
return ['aws', 'k8s', 'test', 'local-docker', 'local-system', 'local'];
}
/**
* Cleans up old cached repositories
* @param maxAgeDays Maximum age in days for cached repositories (default: 30)
*/
static async cleanupCache(maxAgeDays: number = 30): Promise<void> {
await ProviderGitManager.cleanupOldRepositories(maxAgeDays);
}
/**
* Gets information about a provider source without loading it
* @param providerSource The provider source to analyze
* @returns ProviderSourceInfo object with parsed details
*/
static analyzeProviderSource(providerSource: string): ProviderSourceInfo {
return parseProviderSource(providerSource);
}
}