diff --git a/lib/__tests__/apk-parser.test.ts b/lib/__tests__/apk-parser.test.ts new file mode 100644 index 0000000..74aeda5 --- /dev/null +++ b/lib/__tests__/apk-parser.test.ts @@ -0,0 +1,56 @@ +import { parseApkFilename, detectEnvironment } from '../apk-parser'; + +describe('parseApkFilename', () => { + it('should parse APK with environment suffix', () => { + const result = parseApkFilename('ft_timeshift_0_42_57ef3a60d.apk'); + expect(result.environment).toBe('dev'); + expect(result.commit).toBe('57ef3a60d'); + expect(result.isValid).toBe(true); + }); + + it('should parse APK without environment (product)', () => { + const result = parseApkFilename('ft_0_42_ff9ff3441.apk'); + expect(result.environment).toBe('product'); + expect(result.commit).toBe('ff9ff3441'); + expect(result.isValid).toBe(true); + }); + + it('should parse sandbox APK', () => { + const result = parseApkFilename('ft_sandbox_0_42_57ef3a60d.apk'); + expect(result.environment).toBe('sandbox'); + expect(result.commit).toBe('57ef3a60d'); + expect(result.isValid).toBe(true); + }); + + it('should handle invalid format', () => { + const result = parseApkFilename('invalid_filename.apk'); + expect(result.environment).toBe('other'); + expect(result.commit).toBeNull(); + expect(result.isValid).toBe(false); + }); + + it('should handle missing commit ID', () => { + const result = parseApkFilename('ft_dev_0_42.apk'); + expect(result.environment).toBe('dev'); + expect(result.commit).toBeNull(); + expect(result.isValid).toBe(false); + }); +}); + +describe('detectEnvironment', () => { + it('should detect dev environment', () => { + expect(detectEnvironment('ft_timeshift_0_42_57ef3a60d.apk')).toBe('dev'); + }); + + it('should detect sandbox environment', () => { + expect(detectEnvironment('ft_sandbox_0_42_57ef3a60d.apk')).toBe('sandbox'); + }); + + it('should detect product environment (no suffix)', () => { + expect(detectEnvironment('ft_0_42_ff9ff3441.apk')).toBe('product'); + }); + + it('should return other for unknown environments', () => { + expect(detectEnvironment('ft_unknown_0_42_57ef3a60d.apk')).toBe('other'); + }); +}); diff --git a/lib/apk-parser.ts b/lib/apk-parser.ts new file mode 100644 index 0000000..5989f61 --- /dev/null +++ b/lib/apk-parser.ts @@ -0,0 +1,53 @@ +export type ApkEnvironment = 'dev' | 'sandbox' | 'product' | 'other'; + +export interface ParsedApkMetadata { + environment: ApkEnvironment; + commit: string | null; + isValid: boolean; +} + +// Environment mapping based on filename patterns +const ENVIRONMENT_MAP: Record = { + 'timeshift': 'dev', + 'lan': 'dev', + 'dev': 'dev', + 'sandbox': 'sandbox', +}; + +export function detectEnvironment(filename: string): ApkEnvironment { + // Pattern: ft_[environment]_version_commit.apk + // Extract the environment segment between first and second underscores + const match = filename.match(/^ft_([^_]+)_/); + + if (!match) { + // If it doesn't start with ft_ at all, it's an invalid format + if (!filename.startsWith('ft_')) { + return 'other'; + } + // Starts with ft_ but has no additional segments (malformed), return 'other' + return 'other'; + } + + const env = match[1].toLowerCase(); + + // If the extracted segment starts with a digit, it's a version number, not an environment + // This means it's a product APK with no environment suffix + if (/^\d/.test(env)) { + return 'product'; + } + + return ENVIRONMENT_MAP[env] || 'other'; +} + +export function parseApkFilename(filename: string): ParsedApkMetadata { + const environment = detectEnvironment(filename); + const commitMatch = filename.match(/([a-f0-9]{7,})\.apk$/); + const commit = commitMatch ? commitMatch[1] : null; + const isValid = commit !== null; + + return { + environment, + commit, + isValid, + }; +}