UNPKG

appstore-cli

Version:

A command-line interface (CLI) to interact with the Apple App Store Connect API.

201 lines (192 loc) 8.59 kB
#!/usr/bin/env node const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); import * as listApps from './commands/list-apps.js'; import * as getAppDetails from './commands/get-app-details.js'; import * as listBuilds from './commands/list-builds.js'; import * as getBuildDetails from './commands/get-build-details.js'; import * as uploadBuild from './commands/upload-build.js'; import * as listBetaTesters from './commands/list-beta-testers.js'; import * as addBetaTester from './commands/add-beta-tester.js'; import * as removeBetaTester from './commands/remove-beta-tester.js'; import * as listBetaGroups from './commands/list-beta-groups.js'; import * as addTesterToGroup from './commands/add-tester-to-group.js'; import * as downloadSalesReports from './commands/download-sales-reports.js'; import * as downloadFinancialReports from './commands/download-financial-reports.js'; import * as createApp from './commands/create-app.js'; import * as createBetaGroup from './commands/create-beta-group.js'; import * as submitBuild from './commands/submit-build.js'; const { buildConfigureCommand } = require('./cli/build-configure-command.js'); const { buildRunCommand } = require('./cli/build-run-command.js'); const { exportConfigureCommand } = require('./cli/export-configure-command.js'); const { exportRunCommand } = require('./cli/export-run-command.js'); const { certificateConfigureCommand } = require('./cli/certificate-configure-command.js'); const { saveConfig, loadConfig } = require('./config.js'); const fs = require('fs'); yargs(hideBin(process.argv)) .command('hello', 'prints hello world', () => { console.log('hello world'); }) .command('config <subcommand>', 'Manage configuration', (yargs: any) => { yargs .command('set', 'Set API credentials', (yargs: any) => { return yargs .option('keyId', { alias: 'k', describe: 'Your App Store Connect API Key ID', type: 'string', demandOption: false }) .option('issuerId', { alias: 'i', describe: 'Your App Store Connect API Issuer ID', type: 'string', demandOption: false }) .option('privateKey', { alias: 'p', describe: 'Your App Store Connect API Private Key', type: 'string', demandOption: false }) .option('privateKeyPath', { alias: 'P', describe: 'Path to your App Store Connect API Private Key file', type: 'string', demandOption: false }) .option('fastlaneToken', { alias: 'f', describe: 'Fastlane spaceauth session token', type: 'string', demandOption: false }) .option('username', { alias: 'u', describe: 'Apple ID username for authentication', type: 'string', demandOption: false }) .check((argv: any) => { // Ensure we have either API credentials or Fastlane token const hasApiCredentials = argv.keyId && argv.issuerId && (argv.privateKey || argv.privateKeyPath); const hasFastlaneToken = argv.fastlaneToken; if (!hasApiCredentials && !hasFastlaneToken) { throw new Error('You must provide either API credentials (--keyId, --issuerId, and --privateKey/--privateKeyPath) or a Fastlane token (--fastlaneToken)'); } // If we have API credentials, ensure we have all required parts if (argv.keyId || argv.issuerId || argv.privateKey || argv.privateKeyPath) { if (!(argv.keyId && argv.issuerId && (argv.privateKey || argv.privateKeyPath))) { throw new Error('If providing API credentials, you must provide --keyId, --issuerId, and either --privateKey or --privateKeyPath'); } } // Ensure exactly one of privateKey or privateKeyPath is provided if we have API credentials if (argv.privateKey && argv.privateKeyPath) { throw new Error('You must provide exactly one of --privateKey or --privateKeyPath'); } return true; }); }, async (argv: any) => { const { keyId, issuerId, privateKey, privateKeyPath, fastlaneToken, username } = argv; // If privateKeyPath is provided, read the private key from the file let actualPrivateKey: string | undefined; if (privateKeyPath) { if (!fs.existsSync(privateKeyPath)) { throw new Error(`Private key file not found: ${privateKeyPath}`); } actualPrivateKey = fs.readFileSync(privateKeyPath, 'utf8'); if (!actualPrivateKey || !actualPrivateKey.trim()) { throw new Error(`Private key file is empty: ${privateKeyPath}`); } } else if (privateKey) { actualPrivateKey = privateKey as string; } await saveConfig({ keyId, issuerId, ...(actualPrivateKey && { privateKey: actualPrivateKey }), ...(fastlaneToken && { fastlaneToken }), ...(username && { username }) }); console.log('Configuration saved successfully.'); }) .command('get', 'Get current configuration', () => {}, () => { try { const config = loadConfig(); console.log('Current configuration:'); console.log(` Key ID: ${config.keyId}`); console.log(` Issuer ID: ${config.issuerId}`); console.log(''); console.log('Note: Private key is stored securely and not displayed.'); console.log('You can also configure the CLI using environment variables:'); console.log(' APPSTORE_KEY_ID: Your App Store Connect API Key ID'); console.log(' APPSTORE_ISSUER_ID: Your App Store Connect API Issuer ID'); console.log(' APPSTORE_PRIVATE_KEY: Your App Store Connect API Private Key'); } catch (error: any) { console.error(error.message); console.log(''); console.log('You can configure the CLI using environment variables:'); console.log(' APPSTORE_KEY_ID: Your App Store Connect API Key ID'); console.log(' APPSTORE_ISSUER_ID: Your App Store Connect API Issuer ID'); console.log(' APPSTORE_PRIVATE_KEY: Your App Store Connect API Private Key'); } } ) .demandCommand(1, 'You need to specify a subcommand (set or get).'); }) .command('apps', 'Manage apps', (yargs: any) => { yargs .command(listApps) .command(getAppDetails) .command(createApp) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('builds', 'Manage builds', (yargs: any) => { yargs .command(listBuilds) .command(getBuildDetails) .command(uploadBuild) .command(submitBuild) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('build', 'Build and export iOS apps', (yargs: any) => { yargs .command(buildConfigureCommand) .command(buildRunCommand) .command('export', 'Export IPA file', (yargs: any) => { yargs .command(exportConfigureCommand) .command(exportRunCommand) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('certificate', 'Configure certificates and provisioning profiles', (yargs: any) => { yargs .command(certificateConfigureCommand) .demandCommand(1, 'You need to specify a subcommand.'); }) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('testers', 'Manage beta testers', (yargs: any) => { yargs .command(listBetaTesters) .command(addBetaTester) .command(removeBetaTester) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('groups', 'Manage beta groups', (yargs: any) => { yargs .command(listBetaGroups) .command(addTesterToGroup) .command(createBetaGroup) .demandCommand(1, 'You need to specify a subcommand.'); }) .command('reports', 'Download reports', (yargs: any) => { yargs .command(downloadSalesReports) .command(downloadFinancialReports) .demandCommand(1, 'You need to specify a subcommand.'); }) .version() .demandCommand(1) .parse();