tdpw
Version:
CLI tool for uploading Playwright test reports to TestDino platform with TestDino storage support
197 lines • 7.54 kB
JavaScript
;
/**
* Cache ID detection utilities for cache functionality
* Cache ID format: <ciProvider>_<repoName>_<branch>
* Example: gh_playwright-tests_main
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CacheIdDetector = void 0;
const tslib_1 = require("tslib");
const git_1 = require("../collectors/git");
const ci_1 = require("../collectors/ci");
const crypto_1 = tslib_1.__importDefault(require("crypto"));
/**
* Service for detecting and generating cache IDs
*/
class CacheIdDetector {
/**
* Detect cache ID and related metadata from environment.
* Format: <ciProvider>_<repoName>_<branch>
* Supports custom cache ID via TESTDINO_CACHE_ID env var or --cache-id flag.
* Supports custom branch and commit via --branch and --commit flags.
*/
static async detectCacheId(customCacheId, customBranch, customCommit) {
try {
// Check for custom cache ID (highest priority)
const envCustomId = process.env.TESTDINO_CACHE_ID;
if (customCacheId || envCustomId) {
const finalCustomId = customCacheId || envCustomId || 'unknown';
// Still need git/ci metadata for commit, pipelineId, etc.
const metadata = await this.collectMetadata();
return {
cacheId: finalCustomId,
pipelineId: metadata.pipelineId,
commit: customCommit || metadata.commit,
branch: customBranch || metadata.branch,
repository: metadata.repository,
ciProvider: metadata.ciProvider,
source: 'custom',
};
}
// Auto-detect cache ID from environment
return await this.generateCacheId(customBranch, customCommit);
}
catch (error) {
console.warn('⚠️ Cache ID detection failed:', error instanceof Error ? error.message : 'Unknown error');
// Fallback to basic cache ID
return {
cacheId: `local_unknown_${this.generateHash('fallback', 6)}`,
pipelineId: 'unknown',
commit: customCommit || 'unknown',
branch: customBranch || 'unknown',
repository: 'unknown/unknown',
ciProvider: 'local',
source: 'local',
};
}
}
/**
* Collect metadata from Git and CI collectors
*/
static async collectMetadata() {
const gitCollector = new git_1.GitCollector(process.cwd());
const gitMetadata = await gitCollector.getMetadata();
const ciMetadata = ci_1.CiCollector.collect();
return {
commit: gitMetadata.commit?.hash || 'unknown',
branch: gitMetadata.branch || 'unknown',
repository: gitMetadata.repository?.name || 'unknown/unknown',
pipelineId: ciMetadata.pipeline.id || 'unknown',
ciProvider: ciMetadata.provider || 'unknown',
};
}
/**
* Generate cache ID from environment
* Format: <ciProvider>_<repoName>_<branch>
*/
static async generateCacheId(customBranch, customCommit) {
const metadata = await this.collectMetadata();
// Use custom values if provided, otherwise use detected values
const finalBranch = customBranch || metadata.branch;
const finalCommit = customCommit || metadata.commit;
// Extract repo name (without owner)
let repoName = this.extractRepoName(metadata.repository);
// If repo name is unknown, add hash and warn user
if (repoName === 'unknown') {
const hash = this.generateHash('unknown', 6);
repoName = `unknown${hash}`;
console.warn('⚠️ Repository name could not be detected');
console.warn('💡 Use a Git repository or set TESTDINO_CACHE_ID explicitly');
console.warn(` Generated cache ID: ${repoName}`);
}
// Get CI provider prefix
const ciPrefix = this.getCIProviderPrefix(metadata.ciProvider);
// Sanitize branch name for cache ID
const sanitizedBranch = this.sanitizeComponent(finalBranch);
// Build cache ID
const cacheId = `${ciPrefix}_${repoName}_${sanitizedBranch}`;
return {
cacheId,
pipelineId: metadata.pipelineId,
commit: finalCommit,
branch: finalBranch, // Original unsanitized branch
repository: metadata.repository,
ciProvider: metadata.ciProvider,
source: metadata.ciProvider === 'unknown' ? 'local' : 'ci',
};
}
/**
* Extract repository name from full repo string
* "owner/repo" → "repo"
*/
static extractRepoName(fullRepo) {
if (!fullRepo || fullRepo === 'unknown/unknown') {
return 'unknown';
}
const parts = fullRepo.split('/');
return parts[parts.length - 1] || 'unknown';
}
/**
* Get CI provider prefix for cache ID
*/
static getCIProviderPrefix(provider) {
const prefixes = {
'github-actions': 'gh',
'gitlab-ci': 'gl',
jenkins: 'jenkins',
'azure-devops': 'az',
circleci: 'circle',
unknown: 'local',
};
return prefixes[provider] || 'local';
}
/**
* Sanitize component for cache ID (remove special characters)
*/
static sanitizeComponent(component) {
return component
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with dash
.replace(/^-+|-+$/g, '') // Trim dashes
.substring(0, 50); // Max 50 chars
}
/**
* Generate hash from string
*/
static generateHash(input, length) {
const hash = crypto_1.default
.createHash('sha256')
.update(input + Date.now())
.digest('hex');
return hash.substring(0, length);
}
/**
* Validate cache ID format
*/
static validateCacheId(cacheId) {
if (!cacheId || typeof cacheId !== 'string') {
return false;
}
// Allow reasonable length and characters (underscores and dashes)
return (cacheId.length >= 5 &&
cacheId.length <= 150 &&
/^[a-zA-Z0-9\-_]+$/.test(cacheId));
}
/**
* Get cache context information for logging using existing collectors
*/
static async getCacheContext() {
try {
const cacheIdInfo = await this.detectCacheId();
return {
// Cache identification
cacheId: cacheIdInfo.cacheId,
pipelineId: cacheIdInfo.pipelineId,
commit: cacheIdInfo.commit,
branch: cacheIdInfo.branch,
repository: cacheIdInfo.repository,
ciProvider: cacheIdInfo.ciProvider,
source: cacheIdInfo.source,
};
}
catch (_error) {
// Fallback to basic detection if detection fails
return {
cacheId: 'unknown',
pipelineId: 'unknown',
commit: 'unknown',
branch: 'unknown',
repository: 'unknown',
ciProvider: 'local',
source: 'local',
};
}
}
}
exports.CacheIdDetector = CacheIdDetector;
//# sourceMappingURL=build-detector.js.map