appstore-cli
Version:
A command-line interface (CLI) to interact with the Apple App Store Connect API.
176 lines (163 loc) • 6.74 kB
text/typescript
import yargs from 'yargs';
import { ConfigurationValidationMiddleware } from '../middleware/configuration-validation-middleware.js';
import fastlaneService from '../services/fastlane-service.js';
import { FastlaneServiceError } from '../services/fastlane-service.js';
import { safeLogger } from '../security/dataHandler.js';
import configurationService from '../services/configuration-service.js';
import migrateBuildAuthConfig from '../scripts/migrate-build-auth.js';
/**
* CLI command for running the export process.
*/
export const exportRunCommand: yargs.CommandModule<{}, {
'archive-path': string;
'output-path'?: string;
'provisioning-profile-path'?: string;
'certificate-path'?: string;
'certificate-password'?: string;
'use-automatic-signing'?: boolean;
'export-options-plist'?: string;
'log-output'?: string;
}> = {
command: 'run',
describe: 'Execute the export process to create an IPA file',
builder: (yargs) => {
return yargs
.option('archive-path', {
alias: 'a',
describe: 'Path to the archive file',
type: 'string',
demandOption: true,
})
.option('output-path', {
alias: 'o',
describe: 'Path where the IPA file should be saved',
type: 'string',
})
.option('provisioning-profile-path', {
describe: 'Path to the provisioning profile file (.mobileprovision)',
type: 'string',
})
.option('certificate-path', {
describe: 'Path to the signing certificate file (.p12 or .pem)',
type: 'string',
})
.option('certificate-password', {
describe: 'Password for the signing certificate (if encrypted)',
type: 'string',
})
.option('use-automatic-signing', {
describe: 'Whether to use automatic code signing',
type: 'boolean',
})
.option('export-options-plist', {
describe: 'Custom export options plist content',
type: 'string',
})
.option('log-output', {
alias: 'l',
describe: 'Path to save the export log output to a file',
type: 'string',
})
.check((argv) => {
// Validate paths for runtime overrides
if (argv['provisioning-profile-path']) {
if (!require('fs').existsSync(argv['provisioning-profile-path'])) {
throw new Error(`Provisioning profile file not found: ${argv['provisioning-profile-path']}`);
}
if (!argv['provisioning-profile-path'].endsWith('.mobileprovision')) {
throw new Error('Provisioning profile file must have .mobileprovision extension');
}
}
if (argv['certificate-path']) {
if (!require('fs').existsSync(argv['certificate-path'])) {
throw new Error(`Certificate file not found: ${argv['certificate-path']}`);
}
if (!argv['certificate-path'].endsWith('.p12') && !argv['certificate-path'].endsWith('.pem')) {
throw new Error('Certificate file must have .p12 or .pem extension');
}
}
if (argv['certificate-path'] && argv['certificate-path'].endsWith('.p12') &&
argv['certificate-password'] === undefined) {
throw new Error('Certificate password is required for .p12 certificate files');
}
if (argv['use-automatic-signing'] &&
(argv['provisioning-profile-path'] || argv['certificate-path'])) {
throw new Error('Cannot use automatic signing with manual provisioning profile or certificate paths');
}
// Validate log output path
if (argv['log-output']) {
const path = require('path');
const dir = path.dirname(argv['log-output']);
if (!require('fs').existsSync(dir)) {
throw new Error(`Log output directory does not exist: ${dir}`);
}
}
return true;
});
},
handler: async (argv) => {
try {
// Check if we have an old build auth configuration and migrate it
const authConfig = configurationService.loadAuthenticationConfiguration();
if (authConfig) {
console.warn('⚠️ WARNING: Found deprecated build auth configuration.');
console.warn('Migrating to unified appstore-cli config...');
await migrateBuildAuthConfig();
console.warn('Migration completed. You can now remove the separate build auth configuration.');
console.warn('');
}
// Check if we have certificate configuration
let certConfig = configurationService.loadCertificateConfiguration() || {};
// Validate configurations using middleware
const { exportConfig, authConfig: validatedAuthConfig } = ConfigurationValidationMiddleware.validateAllConfigurations();
if (!exportConfig) {
console.error('Export configuration not found. Please run "appstore-cli build export configure" first.');
process.exit(1);
}
// Merge runtime certificate overrides (do not persist)
const runtimeCertOverrides: any = {};
if (argv['provisioning-profile-path']) {
runtimeCertOverrides.provisioningProfilePath = argv['provisioning-profile-path'];
}
if (argv['certificate-path']) {
runtimeCertOverrides.certificatePath = argv['certificate-path'];
}
if (argv['certificate-password']) {
runtimeCertOverrides.certificatePassword = argv['certificate-password'];
}
if (argv['use-automatic-signing'] !== undefined) {
runtimeCertOverrides.useAutomaticSigning = argv['use-automatic-signing'];
}
if (argv['export-options-plist']) {
runtimeCertOverrides.exportOptionsPlist = argv['export-options-plist'];
}
certConfig = { ...certConfig, ...runtimeCertOverrides };
// Execute the export
console.log('Starting export process...');
const ipaPath = await fastlaneService.executeExport(
exportConfig,
validatedAuthConfig || {},
certConfig,
argv['archive-path'],
argv['output-path'],
argv['log-output']
);
// Output success message
console.log(`Export completed successfully. IPA saved to: ${ipaPath}`);
} catch (error) {
if (error instanceof FastlaneServiceError) {
console.error(`Export failed: ${error.message}`);
safeLogger.error('Export failed with FastlaneServiceError', {
error: error.message,
code: error.code
});
} else {
console.error(`Export failed: ${(error as Error).message}`);
safeLogger.error('Export failed with unexpected error', {
error: (error as Error).message
});
}
process.exit(1);
}
},
};