@swell/cli
Version:
Swell's command line interface/utility
158 lines (153 loc) • 6.25 kB
JavaScript
import { confirm } from '@inquirer/prompts';
import { Args, Flags } from '@oclif/core';
import { CLIError } from '@oclif/errors';
import * as fs from 'node:fs';
import ora from 'ora';
import * as semver from 'semver';
import style from '../../lib/style.js';
import { PushAppCommand } from '../../push-app-command.js';
export default class AppRelease extends PushAppCommand {
static args = {
version: Args.string({
description: 'semver version to release',
async parse(input) {
const parsedInput = semver.valid(input);
if (parsedInput) {
return parsedInput;
}
throw new CLIError(`Invalid version: ${input}`);
},
}),
};
static description = `App released are reviewed by the Swell team for quality and functionality before publishing.
If the version does not exist, you will be prompted to create it.
Use the --amend flag to update the description or release notes of an existing release version.
Use the --supported=false flag to indicate the version is not officially supported by the developer.
The app must be connected to a partner account with access to publish.`;
static examples = [
'swell app release',
'swell app release 2.0.0',
'swell app release 2.0.0 --amend --release-notes ./releases/v2.0.0.md',
'swell app release 2.0.0 -y',
];
static flags = {
amend: Flags.boolean({
description: 'amend the description or release notes of an existing release',
}),
message: Flags.string({
char: 'm',
description: 'description of the changes in this version for internal use',
}),
'no-git-tag': Flags.boolean({
description: 'if the version is being created, do not create a git tag',
}),
'not-supported': Flags.boolean({
description: 'indicates the version is not officially supported by the developer',
}),
'release-notes': Flags.string({
description: 'relative path to a file containing release notes',
}),
yes: Flags.boolean({
char: 'y',
description: 'skip confirmation prompt',
}),
};
static summary = 'Release a new version of your an app for publishing to the App Store.';
static orientation = {
env: 'test',
};
async run() {
const { args, flags } = await this.parse(AppRelease);
const { amend, message, 'not-supported': notSupported, 'release-notes': releaseNotesFile, yes, } = flags;
if (!(await this.ensureAppExists(undefined, false))) {
return;
}
let releaseNotes;
if (releaseNotesFile) {
try {
releaseNotes = fs.readFileSync(releaseNotesFile, 'utf8');
}
catch {
this.error(`Release notes could not be loaded from \`${releaseNotesFile}\``);
}
}
const appVersion = await this.validateCreateVersion(args.version, flags);
if (!appVersion) {
return;
}
if (appVersion.released && !amend) {
throw new Error(`Version ${appVersion.version} has already been released. Use --amend to update version details.`);
}
const releaseData = {
description: message || appVersion.description,
release_notes: releaseNotesFile ? releaseNotes : appVersion.release_notes,
supported: notSupported
? false
: appVersion.supported === undefined
? true
: appVersion.supported,
};
if (!amend && !yes) {
const continueVersion = await confirm({
message: `Release ${style.appConfigValue(this.app.name)} version ${appVersion.version}?`,
});
if (!continueVersion) {
return;
}
}
const spinner = ora();
this.log();
spinner.start(`${amend ? 'Updating release' : 'Releasing'} version ${style.appConfigValue(appVersion.version)}`);
await this.updateVersion(appVersion.version, {
...releaseData,
released: true,
}, spinner);
spinner.succeed(`${style.appConfigValue(this.app.name)} release ${appVersion.version} ${amend ? 'updated' : 'released'}.`);
}
validateReleaseDetails() {
if (!this.app.description) {
throw new Error('App must have a description');
}
const requiresPreviewImages = this.app.type === 'storefront' &&
(!this.app.kind || this.app.kind === 'shop') &&
this.app.storefront?.theme.provider !== 'app';
if (requiresPreviewImages) {
if (!this.app.preview_image) {
throw new Error('Storefront app must have a preview image to release');
}
}
else if (this.app.type !== 'theme' && !this.app.logo_icon) {
throw new Error('App must have a logo icon to release');
}
}
async validateCreateVersion(inputVersion, flags) {
let version = inputVersion;
if (!version) {
version = this.app.version;
if (!version) {
throw new Error('No version specified');
}
}
this.validateReleaseDetails();
let appVersion = await this.getVersion(version);
if (!appVersion) {
if (flags.amend) {
throw new Error(`Version ${version} does not exist.`);
}
else {
await this.config.runCommand('app:version', [
version,
...(flags.message ? [`-m ${JSON.stringify(flags.message)}`] : []),
...(flags['no-git-tag'] ? ['--no-git-tag'] : []),
...(flags.yes ? ['-y'] : []),
]);
this.log();
appVersion = await this.getVersion(version);
if (!appVersion) {
throw new Error(`An error occurred while creating version ${version}.`);
}
}
}
return appVersion;
}
}