diff --git a/README.md b/README.md index 0ae55cd..52fe3d6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Expo - Semantic Release [![Latest Release](https://img.shields.io/github/release/byCedric/semantic-release-expo/all.svg?style=flat-square)](https://github.com/byCedric/semantic-release-expo/releases) -[![Build Status](https://img.shields.io/travis/byCedric/semantic-release-expo/master.svg?style=flat-square)](https://travis-ci.com/byCedric/semantic-release-expo) +[![Build Status](https://img.shields.io/travis/com/byCedric/semantic-release-expo/master.svg?style=flat-square)](https://travis-ci.com/byCedric/semantic-release-expo) [![Codecov coverage](https://img.shields.io/codecov/c/github/byCedric/semantic-release-expo.svg?style=flat-square)](https://codecov.io/gh/byCedric/semantic-release-expo) [![Code Climate grade](https://img.shields.io/codeclimate/maintainability/byCedric/semantic-release-expo.svg?style=flat-square)](https://codeclimate.com/github/byCedric/semantic-release-expo) diff --git a/codecov.yml b/codecov.yml index f37e560..29b9078 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,7 +1,7 @@ --- comment: layout: "reach, diff, flags, files" - behavior: default + behavior: spammy require_changes: false require_base: false require_head: true diff --git a/src/expo.ts b/src/expo.ts index f19c048..cb3ee08 100644 --- a/src/expo.ts +++ b/src/expo.ts @@ -1,6 +1,7 @@ import { readFile, readJson, writeJson } from 'fs-extra'; import * as detectIndent from 'detect-indent'; import * as detectNewline from 'detect-newline'; +import { Context } from './types'; /** * A manifest-light definition to use for typehinting. @@ -40,6 +41,15 @@ export const DEFAULT_INDENT = ' '; */ export const DEFAULT_NEWLINE = '\n'; +/** + * Log information about the manifest which is related to the error. + */ +export function logManifestFromError(context: Context, error: any) { + if (error && error.expo) { + context.logger.log('Error encountered for %s manifest %s', 'Expo', error.expo); + } +} + /** * Read the Expo manifest content and return the parsed JSON. */ diff --git a/src/scripts/prepare.ts b/src/scripts/prepare.ts index 3e95c17..6f18b91 100644 --- a/src/scripts/prepare.ts +++ b/src/scripts/prepare.ts @@ -1,10 +1,12 @@ import { getManifestFiles } from '../config'; -import { readManifests, writeManifest } from '../expo'; +import { readManifests, writeManifest, logManifestFromError } from '../expo'; import { SemanticMethod } from '../types'; import bumpVersions from '../version-bumpers'; +const SemanticReleaseError = require('@semantic-release/error'); + /** - * Prepare the new release by updating the manifest. + * Prepare the new release by updating all manifests. * This should update at least the `version` using the next release version name. * It should also update the version code and build number when available. */ @@ -21,7 +23,19 @@ const prepare: SemanticMethod = async (config, context) => { }) )); - await Promise.all(writes); + try { + await Promise.all(writes); + } catch (error) { + logManifestFromError(context, error); + + throw new SemanticReleaseError( + 'Could not write Expo manifest(s)', + 'EWRITEEXPOMANIFEST', + error.message + ); + } + + context.logger.log('Updated all %s manifests!', writes.length); }; export default prepare; diff --git a/src/scripts/verify-conditions.ts b/src/scripts/verify-conditions.ts index 137ff48..ac45c68 100644 --- a/src/scripts/verify-conditions.ts +++ b/src/scripts/verify-conditions.ts @@ -1,12 +1,12 @@ import { getManifestFiles, inheritPrepareConfig } from '../config'; -import { readManifests } from '../expo'; +import { readManifests, logManifestFromError } from '../expo'; import { SemanticMethod } from '../types'; const SemanticReleaseError = require('@semantic-release/error'); /** * Verify the configuration of this plugin. - * This checks if an Expo `app.json` can be found. + * This checks if all Expo manifests are readable. */ const verifyConditions: SemanticMethod = async (config, context) => { const verifyConfig = inheritPrepareConfig(config, context); @@ -16,11 +16,13 @@ const verifyConditions: SemanticMethod = async (config, context) => { meta => context.logger.log('Found %s manifest for %s in %s', 'Expo', meta.manifest.name, meta.filename) ); } catch (error) { - if (error.expo) { - context.logger.log('Error encountered for %s manifest %s', 'Expo', error.expo); - } + logManifestFromError(context, error); - throw new SemanticReleaseError('Could not load Expo manifest(s).', 'EINVALIDEXPOMANIFEST', error.message); + throw new SemanticReleaseError( + 'Could not load Expo manifest(s).', + 'EINVALIDEXPOMANIFEST', + error.message + ); } }; diff --git a/test/expo.test.ts b/test/expo.test.ts index a37cfa9..1deedfc 100644 --- a/test/expo.test.ts +++ b/test/expo.test.ts @@ -12,6 +12,7 @@ import { MANIFEST_FILE, DEFAULT_INDENT, DEFAULT_NEWLINE, + logManifestFromError, readManifest, readManifests, writeManifest, @@ -19,6 +20,7 @@ import { getAndroidPlatform, getIosPlatform, } from '../src/expo'; +import { createContext } from './factory'; describe('expo', () => { describe('constants', () => { @@ -27,6 +29,30 @@ describe('expo', () => { it('has line feed as default new line', () => expect(DEFAULT_NEWLINE).toBe('\n')); }); + describe('#logManifestFromError', () => { + it('does not log anything for normal errors', () => { + const context = createContext(); + + logManifestFromError(context, new Error()); + + expect((context.logger.log as jest.Mock).mock.calls).toHaveLength(0); + }); + + it('does log for errors related to manifest errors', () => { + const context = createContext(); + const error = new Error() as any; + error.expo = 'app.production.json'; + + logManifestFromError(context, error); + + expect(context.logger.log).toBeCalledWith( + 'Error encountered for %s manifest %s', + 'Expo', + 'app.production.json', + ); + }); + }); + describe('#readManifest', () => { it('reads the manifest file', async () => { readFile.mockResolvedValue('{ "expo": { "name": "test" } }'); diff --git a/test/factory.ts b/test/factory.ts new file mode 100644 index 0000000..dc8be75 --- /dev/null +++ b/test/factory.ts @@ -0,0 +1,32 @@ +import { Context } from '../src/types'; + +/** + * Create a simple context logger with mock methods. + */ +export function createContextLogger() { + return { + log: jest.fn(), + error: jest.fn(), + }; +} + +/** + * Create a simple context object with logger. + */ +export function createContext(): Context { + return { logger: createContextLogger() }; +} + +/** + * Create a context with logger and default options. + */ +export function createContextWithOptions(): Context { + return { + logger: createContextLogger(), + options: { + branch: 'master', + repositoryUrl: 'https://github.com/bycedric/semantic-release-expo', + tagFormat: '${version}', + }, + }; +} diff --git a/test/scripts/prepare.test.ts b/test/scripts/prepare.test.ts index 7b9504a..0fcad6e 100644 --- a/test/scripts/prepare.test.ts +++ b/test/scripts/prepare.test.ts @@ -6,21 +6,19 @@ jest.doMock('../../src/expo', () => ({ readManifests, writeManifest, MANIFEST_FI jest.doMock('../../src/version-bumpers', () => ({ default: bumpVersions })); import prepare from '../../src/scripts/prepare'; +import { createContextLogger } from '../factory'; describe('scripts/prepare', () => { it('reads and writes manifests with new version bumped', async () => { const config = {}; const context = { + logger: createContextLogger(), nextRelease: { version: '0.2.1', gitTag: 'v0.2.1', gitHead: 'abc12', notes: 'Testing a new version', }, - logger: { - log: jest.fn(), - error: jest.fn(), - }, }; const oldManifest = { name: 'test', version: '0.2.0' }; diff --git a/test/scripts/verify-conditions.test.ts b/test/scripts/verify-conditions.test.ts index 569b590..69cd130 100644 --- a/test/scripts/verify-conditions.test.ts +++ b/test/scripts/verify-conditions.test.ts @@ -3,31 +3,19 @@ const readManifests = jest.fn(); jest.doMock('../../src/expo', () => ({ readManifests, MANIFEST_FILE: 'app.json' })); import verifyConditions from '../../src/scripts/verify-conditions'; +import { createContextWithOptions } from '../factory'; const SemanticReleaseError = require('@semantic-release/error'); describe('scripts/verify-conditions', () => { - const createContextWithPrepare = (prepare?: any) => ({ - options: { - prepare, - branch: 'master', - repositoryUrl: 'https://github.com/bycedric/semantic-release-expo', - tagFormat: '${version}', - }, - logger: { - log: jest.fn(), - error: jest.fn(), - }, - }); - it('reads manifest and logs name', async () => { + const context = createContextWithOptions(); const config = { manifests: [ 'app.json', 'app.staging.json', ], }; - const context = createContextWithPrepare(); const firstMeta = { filename: 'app.json', @@ -65,7 +53,7 @@ describe('scripts/verify-conditions', () => { it('throws when read manifest failed', async () => { const config = {}; - const context = createContextWithPrepare(); + const context = createContextWithOptions(); readManifests.mockRejectedValue(new Error()); @@ -75,11 +63,13 @@ describe('scripts/verify-conditions', () => { it('inherits prepare configration without verify conditions configuration', async () => { const config = {}; const manifests = ['app.production.json', 'app.staging.json']; - const context = createContextWithPrepare([ + const context = createContextWithOptions(); + + context.options!.prepare = [ { path: '@semantic-release/changelog' }, { path: '@semantic-release/npm' }, { path: 'semantic-release-expo', manifests }, - ]); + ]; const firstMeta = { filename: 'app.production.json',