├── src ├── index.ts ├── version-bumpers │ ├── version.ts │ ├── platform-ios.ts │ ├── platform-android.ts │ └── index.ts ├── scripts │ ├── verify-conditions.ts │ └── prepare.ts ├── config.ts ├── types.ts ├── version.ts └── expo.ts ├── .gitignore ├── codecov.yml ├── test ├── index.test.ts ├── version-bumpers │ ├── version.test.ts │ ├── platform-ios.test.ts │ ├── platform-android.test.ts │ └── index.test.ts ├── scripts │ ├── prepare.test.ts │ └── verify-conditions.test.ts ├── factory.ts ├── config.test.ts ├── expo.test.ts └── version.test.ts ├── commitlint.config.js ├── .editorconfig ├── tsconfig.json ├── .eslintrc.json ├── .travis.yml ├── LICENSE.md ├── CONTRIBUTING.md ├── package.json ├── docs ├── terminal.cast └── terminal.svg ├── README.md └── CHANGELOG.md /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as prepare } from './scripts/prepare'; 2 | export { default as verifyConditions } from './scripts/verify-conditions'; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /coverage 3 | /node_modules 4 | 5 | package-lock.json 6 | npm-debug.log* 7 | 8 | yarn.lock 9 | yarn-debug.log* 10 | yarn-error.log* 11 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | comment: 3 | layout: "reach, diff, flags, files" 4 | behavior: default 5 | require_changes: false 6 | require_base: false 7 | require_head: true 8 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { prepare, verifyConditions } from '../src'; 2 | 3 | describe('index', () => { 4 | it('exports prepare method', () => expect(prepare).toBeInstanceOf(Function)); 5 | it('exports verifyConditions method', () => expect(verifyConditions).toBeInstanceOf(Function)); 6 | }); 7 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@peakfijn/config-commitlint', 4 | ], 5 | ignores: [ 6 | // fix: ignore dependency updates by dependabot 7 | commit => commit.startsWith('chore(deps):'), 8 | commit => commit.startsWith('chore(deps-dev):'), 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | indent_style = space 12 | trim_trailing_whitespace = false 13 | 14 | [*.yml] 15 | indent_size = 2 16 | indent_style = space 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "esModuleInterop": true, 5 | "lib": ["es2015"], 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "outDir": "./build", 9 | "rootDirs": ["./src", "./test"], 10 | "sourceMap": true, 11 | "strict": true, 12 | "target": "es6" 13 | }, 14 | "exclude": [ 15 | "build", 16 | "coverage", 17 | "node_modules" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": [ 4 | "@typescript-eslint" 5 | ], 6 | "env": { 7 | "node": true 8 | }, 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "rules": { 13 | "@typescript-eslint/explicit-function-return-type": "off", 14 | "@typescript-eslint/indent": ["error", "tab"], 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/version-bumpers/version.ts: -------------------------------------------------------------------------------- 1 | import { VersionBumper } from '../types'; 2 | import { calculateVersion } from '../version'; 3 | 4 | const bumpVersion: VersionBumper = (meta, config, context) => { 5 | const newVersion = calculateVersion(meta, config, context); 6 | 7 | context.logger.log( 8 | '%s manifest version changed (%s => %s) in %s', 9 | 'Expo', 10 | meta.manifest.version, 11 | newVersion, 12 | meta.filename, 13 | ); 14 | 15 | return { ...meta.manifest, version: newVersion }; 16 | }; 17 | 18 | export default bumpVersion; 19 | -------------------------------------------------------------------------------- /src/version-bumpers/platform-ios.ts: -------------------------------------------------------------------------------- 1 | import { getIosPlatform } from '../expo'; 2 | import { VersionBumper } from '../types'; 3 | import { calculateIosVersion } from '../version'; 4 | 5 | const bumpPlatformIos: VersionBumper = (meta, config, context) => { 6 | const ios = getIosPlatform(meta.manifest); 7 | const newVersion = calculateIosVersion(meta, config, context); 8 | 9 | context.logger.log( 10 | '%s manifest ios version changed (%s => %s) in %s', 11 | 'Expo', 12 | ios.buildNumber, 13 | newVersion, 14 | meta.filename, 15 | ); 16 | 17 | return { ...meta.manifest, ios: { ...ios, buildNumber: newVersion } }; 18 | }; 19 | 20 | export default bumpPlatformIos; 21 | -------------------------------------------------------------------------------- /src/version-bumpers/platform-android.ts: -------------------------------------------------------------------------------- 1 | import { getAndroidPlatform } from '../expo'; 2 | import { VersionBumper } from '../types'; 3 | import { calculateAndroidVersion } from '../version'; 4 | 5 | const bumpPlatformAndroid: VersionBumper = (meta, config, context) => { 6 | const android = getAndroidPlatform(meta.manifest); 7 | const newVersion = parseInt(calculateAndroidVersion(meta, config, context), 10); 8 | 9 | context.logger.log( 10 | '%s manifest android version changed (%s => %s) in %s', 11 | 'Expo', 12 | android.versionCode, 13 | newVersion, 14 | meta.filename, 15 | ); 16 | 17 | return { ...meta.manifest, android: { ...android, versionCode: newVersion } }; 18 | }; 19 | 20 | export default bumpPlatformAndroid; 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - 10 5 | - 12 6 | - 13 7 | cache: 8 | directories: 9 | - ~/.npm 10 | install: 11 | - npm install --global npm@latest 12 | - npm audit --production 13 | - npm ci 14 | script: 15 | - npx commitlint-travis 16 | - npm run lint 17 | - npm test -- --coverage 18 | - npm run build 19 | after_success: 20 | - npx codecov 21 | deploy: 22 | - provider: script 23 | skip_cleanup: true 24 | script: npx semantic-release --dry-run --branch develop 25 | on: 26 | node: 12 27 | branch: develop 28 | - provider: script 29 | skip_cleanup: true 30 | script: 31 | - npx semantic-release --branch master 32 | on: 33 | branch: master 34 | node: 12 35 | -------------------------------------------------------------------------------- /src/version-bumpers/index.ts: -------------------------------------------------------------------------------- 1 | import { getPlatforms } from '../expo'; 2 | import { VersionBumper } from '../types'; 3 | 4 | import bumpAndroid from './platform-android'; 5 | import bumpIos from './platform-ios'; 6 | import bumpVersion from './version'; 7 | 8 | /** 9 | * Update all versions from the manifest and return an updated version. 10 | * This will check if the manifest supports android and/or ios before applying. 11 | */ 12 | const bumpVersions: VersionBumper = (meta, config, context) => { 13 | const platforms = getPlatforms(meta.manifest); 14 | let newManifest = bumpVersion(meta, config, context); 15 | 16 | if (platforms.indexOf('android') >= 0) { 17 | newManifest = bumpAndroid({ ...meta, manifest: newManifest }, config, context); 18 | } 19 | 20 | if (platforms.indexOf('ios') >= 0) { 21 | newManifest = bumpIos({ ...meta, manifest: newManifest }, config, context); 22 | } 23 | 24 | return newManifest; 25 | }; 26 | 27 | export default bumpVersions; 28 | -------------------------------------------------------------------------------- /src/scripts/verify-conditions.ts: -------------------------------------------------------------------------------- 1 | import { getManifestFiles, inheritPrepareConfig } from '../config'; 2 | import { logManifestFromError, readManifests } from '../expo'; 3 | import { SemanticMethod } from '../types'; 4 | 5 | const SemanticReleaseError = require('@semantic-release/error'); 6 | 7 | /** 8 | * Verify the configuration of this plugin. 9 | * This checks if all Expo manifests are readable. 10 | */ 11 | const verifyConditions: SemanticMethod = async (config, context) => { 12 | const verifyConfig = inheritPrepareConfig(config, context); 13 | 14 | try { 15 | (await readManifests(getManifestFiles(verifyConfig))).forEach( 16 | (meta) => { 17 | context.logger.log( 18 | 'Found %s manifest for %s in %s', 19 | 'Expo', 20 | meta.manifest.name, 21 | meta.filename, 22 | ); 23 | }, 24 | ); 25 | } catch (error) { 26 | logManifestFromError(context, error); 27 | 28 | throw new SemanticReleaseError( 29 | 'Could not load Expo manifest(s).', 30 | 'EINVALIDEXPOMANIFEST', 31 | error.message, 32 | ); 33 | } 34 | }; 35 | 36 | export default verifyConditions; 37 | -------------------------------------------------------------------------------- /test/version-bumpers/version.test.ts: -------------------------------------------------------------------------------- 1 | const calculateVersion = jest.fn(); 2 | 3 | jest.doMock('../../src/version', () => ({ calculateVersion })); 4 | 5 | import bumpVersion from '../../src/version-bumpers/version'; 6 | import { createConfig, createContext, createManifestMeta } from '../factory'; 7 | 8 | describe('version-bumpers/version', () => { 9 | it('returns new manifest with bumped version', () => { 10 | const config = createConfig(); 11 | const context = createContext(); 12 | const meta = createManifestMeta({ 13 | anything: 'else', 14 | name: 'test', 15 | version: context.lastRelease!.version, 16 | }); 17 | 18 | calculateVersion.mockReturnValue('newversion'); 19 | 20 | const manifest = bumpVersion(meta, config, context); 21 | 22 | expect(calculateVersion).toBeCalledWith(meta, config, context); 23 | expect(context.logger.log).toBeCalledWith( 24 | '%s manifest version changed (%s => %s) in %s', 25 | 'Expo', 26 | meta.manifest.version, 27 | 'newversion', 28 | meta.filename, 29 | ); 30 | 31 | expect(manifest).toMatchObject({ 32 | anything: 'else', 33 | name: 'test', 34 | version: 'newversion', 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | 4 | Copyright (c) 2018 Cedric van Putten 5 | 6 | > Permission is hereby granted, free of charge, to any person obtaining a copy 7 | > of this software and associated documentation files (the "Software"), to deal 8 | > in the Software without restriction, including without limitation the rights 9 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | > copies of the Software, and to permit persons to whom the Software is 11 | > furnished to do so, subject to the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be included in 14 | > all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | > THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/scripts/prepare.ts: -------------------------------------------------------------------------------- 1 | import { getManifestFiles } from '../config'; 2 | import { logManifestFromError, readManifests, writeManifest } from '../expo'; 3 | import { SemanticMethod } from '../types'; 4 | import bumpVersions from '../version-bumpers'; 5 | 6 | const SemanticReleaseError = require('@semantic-release/error'); 7 | 8 | /** 9 | * Prepare the new release by updating all manifests. 10 | * This should update at least the `version` using the next release version name. 11 | * It should also update the version code and build number when available. 12 | */ 13 | const prepare: SemanticMethod = async (config, context) => { 14 | const files = await readManifests(getManifestFiles(config)); 15 | const writes = files.map((meta) => ( 16 | writeManifest(meta, bumpVersions(meta, config, context)).then(() => { 17 | context.logger.log( 18 | 'New %s manifest written for %s to %s', 19 | 'Expo', 20 | meta.manifest.name, 21 | meta.filename, 22 | ); 23 | }) 24 | )); 25 | 26 | try { 27 | await Promise.all(writes); 28 | } catch (error) { 29 | logManifestFromError(context, error); 30 | 31 | throw new SemanticReleaseError( 32 | 'Could not write Expo manifest(s)', 33 | 'EWRITEEXPOMANIFEST', 34 | error.message, 35 | ); 36 | } 37 | 38 | context.logger.log('Updated all %s manifests!', writes.length); 39 | }; 40 | 41 | export default prepare; 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | We accept contributions via Pull Requests on [Github](https://github.com/bycedric/semantic-release-expo). 5 | 6 | ## Issues and bugs 7 | 8 | Found a bug or other issue? You can help getting it fixed by submitting an issue! 9 | We really appreciate any help even if you are not able to fix it yourself. 10 | However, if you are able to fix it yourself feel free to create a pull request with a reference to the issue. 11 | 12 | ### Submitting an issue 13 | 14 | Before you create a new issue, try and search if similar issues have been submitted earlier. 15 | Sometimes it is best to join in on an existing discussion instead of creating duplicate issues. 16 | In case you want to create a new issue please provide as much information as requested in the template. 17 | 18 | ### Submitting a pull request 19 | 20 | When you create a new pull request, always make sure you are referencing the accompanying issue. 21 | That way we can keep discussions about the issue separately from code reviews. 22 | Make sure you follow everything and provide as much information as requested in the template. 23 | 24 | ## Commit message guidelines 25 | 26 | This project is following the [conventional commit](https://conventionalcommits.org/) guidelines to increase maintainability and make the history readable. 27 | Please write commit subject/description in succinct English, no capitalization, no punctuation at the end and imperative present tense. 28 | -------------------------------------------------------------------------------- /test/version-bumpers/platform-ios.test.ts: -------------------------------------------------------------------------------- 1 | const getIosPlatform = jest.fn(); 2 | const calculateIosVersion = jest.fn(); 3 | 4 | jest.doMock('../../src/expo', () => ({ getIosPlatform })); 5 | jest.doMock('../../src/version', () => ({ calculateIosVersion })); 6 | 7 | import bumpPlatformIos from '../../src/version-bumpers/platform-ios'; 8 | import { createConfig, createContext, createManifestMeta } from '../factory'; 9 | 10 | describe('version-bumpers/platform-ios', () => { 11 | it('returns new manifest with bumped ios version', () => { 12 | const config = createConfig(); 13 | const context = createContext(); 14 | const meta = createManifestMeta({ 15 | android: { versionCode: 6 }, 16 | ios: { buildNumber: context.lastRelease!.version }, 17 | name: 'test', 18 | version: context.lastRelease!.version, 19 | }); 20 | 21 | getIosPlatform.mockReturnValue(meta.manifest.ios); 22 | calculateIosVersion.mockReturnValue('newversion'); 23 | 24 | const manifest = bumpPlatformIos(meta, config, context); 25 | 26 | expect(getIosPlatform).toBeCalledWith(meta.manifest); 27 | expect(calculateIosVersion).toBeCalledWith(meta, config, context); 28 | expect(context.logger.log).toBeCalledWith( 29 | '%s manifest ios version changed (%s => %s) in %s', 30 | 'Expo', 31 | meta.manifest.ios!.buildNumber, 32 | 'newversion', 33 | meta.filename, 34 | ); 35 | 36 | expect(manifest).toMatchObject({ 37 | android: { versionCode: 6 }, 38 | ios: { buildNumber: 'newversion' }, 39 | name: 'test', 40 | version: context.lastRelease!.version, 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/scripts/prepare.test.ts: -------------------------------------------------------------------------------- 1 | const readManifests = jest.fn(); 2 | const writeManifest = jest.fn(); 3 | const bumpVersions = jest.fn(); 4 | 5 | jest.doMock('../../src/expo', () => ({ readManifests, writeManifest, MANIFEST_FILE: 'app.json' })); 6 | jest.doMock('../../src/version-bumpers', () => bumpVersions); 7 | 8 | import prepare from '../../src/scripts/prepare'; 9 | import { createConfig, createContext, createManifestMeta } from '../factory'; 10 | 11 | describe('scripts/prepare', () => { 12 | it('reads and writes manifests with new version bumped', async () => { 13 | const config = createConfig(); 14 | const context = createContext({ 15 | last: { 16 | gitHead: 'abc123', 17 | gitTag: 'v0.2.0', 18 | version: '0.2.0', 19 | }, 20 | next: { 21 | gitHead: 'abc234', 22 | gitTag: 'v0.2.1', 23 | notes: 'Testing a new version', 24 | version: '0.2.1', 25 | }, 26 | }); 27 | 28 | const oldMeta = createManifestMeta({ name: 'test', version: '0.2.0' }); 29 | const newMeta = createManifestMeta({ name: 'test', version: '0.2.1' }); 30 | 31 | readManifests.mockResolvedValue([oldMeta]); 32 | bumpVersions.mockReturnValue(newMeta.manifest); 33 | writeManifest.mockResolvedValue(undefined); 34 | 35 | await prepare(config, context); 36 | 37 | expect(readManifests).toBeCalled(); 38 | expect(bumpVersions).toBeCalledWith(oldMeta, config, context); 39 | expect(writeManifest).toBeCalledWith(oldMeta, newMeta.manifest); 40 | 41 | expect(context.logger.log).toBeCalledWith( 42 | 'New %s manifest written for %s to %s', 43 | 'Expo', 44 | 'test', 45 | 'app.json', 46 | ); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/version-bumpers/platform-android.test.ts: -------------------------------------------------------------------------------- 1 | const getAndroidPlatform = jest.fn(); 2 | const calculateAndroidVersion = jest.fn(); 3 | 4 | jest.doMock('../../src/expo', () => ({ getAndroidPlatform })); 5 | jest.doMock('../../src/version', () => ({ calculateAndroidVersion })); 6 | 7 | import bumpPlatformAndroid from '../../src/version-bumpers/platform-android'; 8 | import { createConfig, createContext, createManifestMeta } from '../factory'; 9 | 10 | describe('version-bumpers/platform-android', () => { 11 | it('returns new manifest with bumped android version', () => { 12 | const config = createConfig(); 13 | const context = createContext({ 14 | last: { 15 | gitHead: '12asd1', 16 | gitTag: 'v1.2.0', 17 | version: '1.2.0', 18 | }, 19 | next: { 20 | gitHead: 'asd123', 21 | gitTag: 'v1.3.0', 22 | notes: 'New version', 23 | version: '1.3.0', 24 | }, 25 | }); 26 | 27 | const meta = createManifestMeta({ 28 | android: { versionCode: 290010200 }, 29 | ios: { buildNumber: '1.2.0' }, 30 | name: 'test', 31 | sdkVersion: '29.0.0', 32 | version: '1.2.0', 33 | }); 34 | 35 | getAndroidPlatform.mockReturnValue(meta.manifest.android); 36 | calculateAndroidVersion.mockReturnValue('290010300'); 37 | 38 | const manifest = bumpPlatformAndroid(meta, config, context); 39 | 40 | expect(getAndroidPlatform).toBeCalledWith(meta.manifest); 41 | expect(calculateAndroidVersion).toBeCalledWith(meta, config, context); 42 | expect(context.logger.log).toBeCalledWith( 43 | '%s manifest android version changed (%s => %s) in %s', 44 | 'Expo', 45 | meta.manifest.android!.versionCode, 46 | 290010300, 47 | meta.filename, 48 | ); 49 | 50 | expect(manifest).toMatchObject({ 51 | android: { versionCode: 290010300 }, 52 | ios: { buildNumber: '1.2.0' }, 53 | name: 'test', 54 | sdkVersion: '29.0.0', 55 | version: '1.2.0', 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { MANIFEST_FILE } from './expo'; 2 | import { Config, Context, VersionTemplates } from './types'; 3 | 4 | /** 5 | * Get the manifest files that needs to be updated from the configuration. 6 | * If this wasn't set, or is empty, the default manifest file will be returned. 7 | */ 8 | export function getManifestFiles(config: Config): string[] { 9 | if (!config.manifests || config.manifests.length <= 0) { 10 | return [MANIFEST_FILE]; 11 | } 12 | 13 | return config.manifests; 14 | } 15 | 16 | /** 17 | * Get the template functions that builds the platform specific build numbers. 18 | */ 19 | export function getVersionTemplates(config?: Config): VersionTemplates { 20 | const template = config && config.versions; 21 | const fallback = typeof template === 'string' ? template : '${recommended}'; 22 | 23 | return { 24 | android: (typeof template === 'object' && template.android) ? template.android : fallback, 25 | ios: (typeof template === 'object' && template.ios) ? template.ios : fallback, 26 | version: (typeof template === 'object' && template.version) ? template.version : fallback, 27 | }; 28 | } 29 | 30 | /** 31 | * Get the full prepare configuration of the prepare step for this extension. 32 | * It's mostly used for the verify conditions step to inherit the configuration. 33 | */ 34 | export function getPrepareConfig(context: Context): Config | undefined { 35 | if (context.options && context.options.prepare) { 36 | const plugins = Array.isArray(context.options.prepare) 37 | ? context.options.prepare 38 | : [context.options.prepare]; 39 | 40 | return plugins.find((config) => config.path && config.path === 'semantic-release-expo'); 41 | } 42 | } 43 | 44 | /** 45 | * Inherit all configuration from the possible prepare step. 46 | * This makes sure we don't have to copy all settings when verifying. 47 | */ 48 | export function inheritPrepareConfig(config: Config, context: Context): Config { 49 | const prepareConfig = getPrepareConfig(context) || {}; 50 | 51 | return { 52 | manifests: config.manifests || prepareConfig.manifests, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "semantic-release-expo", 3 | "version": "2.2.3", 4 | "description": "An Expo implementation for semantic release, so you don't have to bother", 5 | "keywords": [ 6 | "expo", 7 | "react", 8 | "native", 9 | "semantic", 10 | "release", 11 | "bycedric" 12 | ], 13 | "author": "Cedric van Putten ", 14 | "license": "MIT", 15 | "homepage": "https://github.com/bycedric/semantic-release-expo#readme", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/bycedric/semantic-release-expo.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/bycedric/semantic-release-expo/issues" 22 | }, 23 | "main": "./build/src/index.js", 24 | "types": "./build/src/index.d.ts", 25 | "files": [ 26 | "build/src" 27 | ], 28 | "scripts": { 29 | "lint": "tsc --noEmit && eslint src test --ext js,ts", 30 | "test": "jest", 31 | "build": "tsc --build", 32 | "commit": "git-cz" 33 | }, 34 | "dependencies": { 35 | "detect-indent": "^6.0.0", 36 | "detect-newline": "^3.0.0", 37 | "fs-extra": "^8.1.0", 38 | "lodash": "^4.17.15", 39 | "semver": "^7.1.1" 40 | }, 41 | "devDependencies": { 42 | "@commitlint/travis-cli": "^8.1.0", 43 | "@peakfijn/config-commitizen": "^2.1.0", 44 | "@peakfijn/config-commitlint": "^2.1.0", 45 | "@peakfijn/config-release": "^2.1.0", 46 | "@types/detect-indent": "^6.0.0", 47 | "@types/fs-extra": "^8.0.0", 48 | "@types/jest": "^24.0.11", 49 | "@types/lodash": "^4.14.136", 50 | "@types/node": "^13.1.2", 51 | "@types/semver": "^6.0.1", 52 | "@typescript-eslint/eslint-plugin": "^1.13.0", 53 | "@typescript-eslint/parser": "^1.13.0", 54 | "codecov": "^3.5.0", 55 | "eslint": "^6.1.0", 56 | "jest": "^24.8.0", 57 | "ts-jest": "^24.0.2", 58 | "typescript": "^3.5.3" 59 | }, 60 | "jest": { 61 | "collectCoverageFrom": [ 62 | "src/**/*.{ts,tsx}" 63 | ], 64 | "moduleFileExtensions": [ 65 | "ts", 66 | "tsx", 67 | "js", 68 | "json" 69 | ], 70 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 71 | "testURL": "http://localhost/", 72 | "transform": { 73 | "^.+\\.tsx?$": "ts-jest" 74 | } 75 | }, 76 | "config": { 77 | "commitizen": { 78 | "path": "@peakfijn/config-commitizen" 79 | } 80 | }, 81 | "release": { 82 | "extends": "@peakfijn/config-release" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/factory.ts: -------------------------------------------------------------------------------- 1 | import { Manifest, ManifestMeta } from '../src/expo'; 2 | import { Config, Context, LastRelease, NextRelease } from '../src/types'; 3 | 4 | /** 5 | * Create an empty object representing the config. 6 | * This is mostly for future configuration and not-having-to-rewrite the tests. 7 | */ 8 | export function createConfig(): Config { 9 | return {}; 10 | } 11 | 12 | /** 13 | * Create a context partial with a mocked logger. 14 | */ 15 | export function createContextLogger() { 16 | return { 17 | logger: { 18 | error: jest.fn(), 19 | log: jest.fn(), 20 | }, 21 | }; 22 | } 23 | 24 | /** 25 | * Create a context partial with some general repository options. 26 | */ 27 | export function createContextOptions() { 28 | return { 29 | options: { 30 | branch: 'master', 31 | repositoryUrl: 'https://github.com/bycedric/semantic-release-expo', 32 | tagFormat: '${version}', 33 | }, 34 | }; 35 | } 36 | 37 | /** 38 | * Create a context partial which defines the next release. 39 | */ 40 | export function createContextNextRelease(options?: NextRelease) { 41 | return { 42 | nextRelease: { 43 | gitHead: options ? options.gitHead : 'abc234', 44 | gitTag: options ? options.version : 'v1.2.0', 45 | notes: options ? options.notes : 'Testing notes', 46 | version: options ? options.version : '1.2.0', 47 | }, 48 | }; 49 | } 50 | 51 | /** 52 | * Create a context partial which defines the last release. 53 | */ 54 | export function createContextLastRelease(options?: LastRelease) { 55 | return { 56 | lastRelease: { 57 | gitHead: options ? options.gitHead : 'abc123', 58 | gitTag: options ? options.version : 'v1.1.3', 59 | version: options ? options.version : '1.1.3', 60 | }, 61 | }; 62 | } 63 | 64 | /** 65 | * Create a manifest meta object containing the manifest data. 66 | */ 67 | export function createManifestMeta(manifest: Manifest): ManifestMeta { 68 | return { 69 | content: JSON.stringify(manifest), 70 | filename: 'app.json', 71 | manifest, 72 | }; 73 | } 74 | 75 | /** 76 | * Create a (full) context object with logger, options and last/next releases. 77 | */ 78 | export function createContext(options: { next?: NextRelease; last?: LastRelease } = {}): Context { 79 | return { 80 | ...createContextLogger(), 81 | ...createContextOptions(), 82 | ...createContextNextRelease(options.next), 83 | ...createContextLastRelease(options.last), 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { Manifest, ManifestMeta } from './expo'; 2 | 3 | /** 4 | * The semantic release configuration itself. 5 | */ 6 | export interface GlobalConfig { 7 | /** The full prepare step configuration. */ 8 | prepare?: any; 9 | /** The branch on which releases should happen. */ 10 | branch: string; 11 | /** The Git repository URL, in any supported format. */ 12 | repositoryUrl: string; 13 | /** The Git tag format used by semantic-release to identify releases. */ 14 | tagFormat: string; 15 | } 16 | 17 | export interface Config { 18 | /** The manifest file(s) to update. */ 19 | manifests?: string[]; 20 | /** The (lodash) version template formats for all version outputs. */ 21 | versions?: string | { 22 | /** The manifest version template to use. */ 23 | version?: string; 24 | /** The android-specific version (code) template to use. */ 25 | android?: string; 26 | /** The ios-specific build number template to use. */ 27 | ios?: string; 28 | }; 29 | } 30 | 31 | export interface LastRelease { 32 | /** The version name of the release */ 33 | version: string; 34 | /** The Git tag of the release. */ 35 | gitTag: string; 36 | /** The Git checksum of the last commit of the release. */ 37 | gitHead: string; 38 | } 39 | 40 | export interface NextRelease extends LastRelease { 41 | /** The release notes of the next release. */ 42 | notes: string; 43 | } 44 | 45 | export interface Context { 46 | /** The semantic release configuration itself. */ 47 | options?: GlobalConfig; 48 | /** The previous release details. */ 49 | lastRelease?: LastRelease; 50 | /** The next release details. */ 51 | nextRelease?: NextRelease; 52 | /** The shared logger instance of semantic release. */ 53 | logger: { 54 | log: (message: string, ...vars: any[]) => void; 55 | error: (message: string, ...vars: any[]) => void; 56 | }; 57 | } 58 | 59 | /** 60 | * A method which is used by semantic releases as script execution. 61 | * This is loaded and injected by semantic itself. 62 | */ 63 | export type SemanticMethod = (config: Config, context: Context) => any; 64 | 65 | /** 66 | * A method that updates a single manifest version number. 67 | * This can either be in general or platform specific like android and ios. 68 | */ 69 | export type VersionBumper = (meta: ManifestMeta, config: Config, context: Context) => Manifest; 70 | 71 | /** 72 | * A method that can calculate a new version (tag) based on a template. 73 | * It also receives all information related to current version, configuration and context. 74 | */ 75 | export type VersionCalculator = (meta: ManifestMeta, config: Config, context: Context) => string; 76 | 77 | export interface VersionTemplates { 78 | /** The manifest version template to use. */ 79 | version: string; 80 | /** The android-specific version (code) template to use. */ 81 | android: string; 82 | /** The ios-specific build number template to use. */ 83 | ios: string; 84 | } 85 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | import { template as _template } from 'lodash'; 2 | import { coerce, SemVer } from 'semver'; 3 | import { getVersionTemplates } from './config'; 4 | import { getAndroidPlatform, getIosPlatform, ManifestMeta } from './expo'; 5 | import { Context, VersionCalculator } from './types'; 6 | 7 | /** 8 | * Calculate a numeric version code based on the next release and used expo version. 9 | * This uses the versioning specifically designed for Android version codes. 10 | * 11 | * @see https://medium.com/@maxirosson/versioning-android-apps-d6ec171cfd82 12 | */ 13 | export const getVersionCode = (next: SemVer, expo: SemVer) => ( 14 | expo.major * 10000000 + next.major * 10000 + next.minor * 100 + next.patch 15 | ); 16 | 17 | /** 18 | * Get the default (template) variables for all platforms. 19 | * This includes the recommended next release string and numeric version code. 20 | */ 21 | export const getDefaultVariables = (meta: ManifestMeta, context: Context) => { 22 | const expo = coerce(meta.manifest.sdkVersion); 23 | const last = coerce(context.lastRelease!.version); 24 | const next = coerce(context.nextRelease!.version); 25 | 26 | return { 27 | code: (next && expo) ? getVersionCode(next, expo) : '000000000', 28 | expo, last, next, 29 | recommended: context.nextRelease!.version, 30 | }; 31 | }; 32 | 33 | /** 34 | * Calculate the (next) version for the "root" expo manifest version. 35 | * It's recommended to use the next release version string. 36 | */ 37 | export const calculateVersion: VersionCalculator = (meta, config, context) => { 38 | const { version } = getVersionTemplates(config); 39 | 40 | return _template(version)({ 41 | ...getDefaultVariables(meta, context), 42 | increment: (Number(meta.manifest.version) || 0) + 1, 43 | }); 44 | }; 45 | 46 | /** 47 | * Calculate the (next) version for the android platform. 48 | * Although incremental numbers are supported, they are discouraged because of non-deterministic behaviour. 49 | * Its highly recommended to use the (calculatable) version code. 50 | */ 51 | export const calculateAndroidVersion: VersionCalculator = (meta, config, context) => { 52 | const { android } = getVersionTemplates(config); 53 | const androidConfig = getAndroidPlatform(meta.manifest); 54 | const variables = getDefaultVariables(meta, context); 55 | 56 | return _template(android)({ 57 | ...variables, 58 | increment: (Number(androidConfig.versionCode) || 0) + 1, 59 | recommended: variables.code, 60 | }); 61 | }; 62 | 63 | /** 64 | * Calculate the (next) version for the ios platform. 65 | * Although incremental numbers are supported, they are discouraged because of non-deterministic behaviour. 66 | * Its recommended to use the next release version string. 67 | */ 68 | export const calculateIosVersion: VersionCalculator = (meta, config, context) => { 69 | const { ios } = getVersionTemplates(config); 70 | const iosConfig = getIosPlatform(meta.manifest); 71 | 72 | return _template(ios)({ 73 | ...getDefaultVariables(meta, context), 74 | increment: (Number(iosConfig.buildNumber) || 0) + 1, 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /test/scripts/verify-conditions.test.ts: -------------------------------------------------------------------------------- 1 | const readManifests = jest.fn(); 2 | 3 | jest.doMock('../../src/expo', () => ({ readManifests, MANIFEST_FILE: 'app.json' })); 4 | 5 | import verifyConditions from '../../src/scripts/verify-conditions'; 6 | import { createContext } from '../factory'; 7 | 8 | describe('scripts/verify-conditions', () => { 9 | it('reads manifest and logs name', async () => { 10 | const context = createContext(); 11 | const config = { 12 | manifests: [ 13 | 'app.json', 14 | 'app.staging.json', 15 | ], 16 | }; 17 | 18 | const firstMeta = { 19 | content: '{ "name": "test" }', 20 | filename: 'app.json', 21 | manifest: { name: 'test' }, 22 | }; 23 | 24 | const secondMeta = { 25 | content: '{ "name": "test-staging" }', 26 | filename: 'app.staging.json', 27 | manifest: { name: 'test-staging' }, 28 | }; 29 | 30 | readManifests.mockResolvedValue([firstMeta, secondMeta]); 31 | 32 | await verifyConditions(config, context); 33 | 34 | expect(readManifests).toBeCalled(); 35 | expect(context.logger.log).toHaveBeenNthCalledWith( 36 | 1, 37 | 'Found %s manifest for %s in %s', 38 | 'Expo', 39 | 'test', 40 | 'app.json', 41 | ); 42 | 43 | expect(context.logger.log).toHaveBeenNthCalledWith( 44 | 2, 45 | 'Found %s manifest for %s in %s', 46 | 'Expo', 47 | 'test-staging', 48 | 'app.staging.json', 49 | ); 50 | }); 51 | 52 | it('throws when read manifest failed', async () => { 53 | const config = {}; 54 | const context = createContext(); 55 | 56 | readManifests.mockRejectedValue(new Error()); 57 | 58 | expect(verifyConditions(config, context)).rejects.toThrowError(); 59 | }); 60 | 61 | it('inherits prepare configration without verify conditions configuration', async () => { 62 | const config = {}; 63 | const manifests = ['app.production.json', 'app.staging.json']; 64 | const context = createContext(); 65 | 66 | context.options!.prepare = [ 67 | { path: '@semantic-release/changelog' }, 68 | { path: '@semantic-release/npm' }, 69 | { path: 'semantic-release-expo', manifests }, 70 | ]; 71 | 72 | const firstMeta = { 73 | content: '{ "name": "test" }', 74 | filename: 'app.production.json', 75 | manifest: { name: 'test' }, 76 | }; 77 | 78 | const secondMeta = { 79 | content: '{ "name": "test-staging" }', 80 | filename: 'app.staging.json', 81 | manifest: { name: 'test-staging' }, 82 | }; 83 | 84 | readManifests.mockResolvedValue([firstMeta, secondMeta]); 85 | 86 | await verifyConditions(config, context); 87 | 88 | expect(readManifests).toBeCalled(); 89 | expect(context.logger.log).toHaveBeenNthCalledWith( 90 | 1, 91 | 'Found %s manifest for %s in %s', 92 | 'Expo', 93 | 'test', 94 | 'app.production.json', 95 | ); 96 | 97 | expect(context.logger.log).toHaveBeenNthCalledWith( 98 | 2, 99 | 'Found %s manifest for %s in %s', 100 | 'Expo', 101 | 'test-staging', 102 | 'app.staging.json', 103 | ); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /test/version-bumpers/index.test.ts: -------------------------------------------------------------------------------- 1 | const getPlatforms = jest.fn(); 2 | const bumpPlatformAndroid = jest.fn(); 3 | const bumpPlatformIos = jest.fn(); 4 | const bumpVersion = jest.fn(); 5 | 6 | jest.doMock('../../src/expo', () => ({ getPlatforms })); 7 | jest.doMock('../../src/version-bumpers/platform-android', () => bumpPlatformAndroid); 8 | jest.doMock('../../src/version-bumpers/platform-ios', () => bumpPlatformIos); 9 | jest.doMock('../../src/version-bumpers/version', () => bumpVersion); 10 | 11 | import bumpAllVersion from '../../src/version-bumpers'; 12 | import { createConfig, createContext, createManifestMeta } from '../factory'; 13 | 14 | describe('version-bumpers', () => { 15 | it('returns new manifest with bumped version', () => { 16 | const config = createConfig(); 17 | const context = createContext({ 18 | next: { 19 | gitHead: 'abc12', 20 | gitTag: 'v9.1.0', 21 | notes: 'Testing a new version', 22 | version: '9.1.0', 23 | }, 24 | }); 25 | 26 | const oldMeta = createManifestMeta({ name: 'test', version: '9.0.0' }); 27 | const newMeta = createManifestMeta({ name: 'test', version: '9.1.9' }); 28 | 29 | getPlatforms.mockReturnValue([]); 30 | bumpVersion.mockReturnValue(newMeta.manifest); 31 | 32 | const received = bumpAllVersion(oldMeta, config, context); 33 | 34 | expect(received).toBe(newMeta.manifest); 35 | expect(bumpVersion).toBeCalledWith(oldMeta, config, context); 36 | }); 37 | 38 | it('returns new manifest with bumped version and platform versions', () => { 39 | const config = createConfig(); 40 | const context = createContext({ 41 | next: { 42 | gitHead: 'abc12', 43 | gitTag: 'v2.4.0', 44 | notes: 'Testing a new version', 45 | version: '2.4.0', 46 | }, 47 | }); 48 | 49 | const oldMeta = createManifestMeta({ 50 | android: { versionCode: 12 }, 51 | ios: { buildNumber: '2.3.0' }, 52 | name: 'test', 53 | version: '2.3.0', 54 | }); 55 | 56 | // reuse the old manifest meta, stringified contents should match the exact file content (not updated one). 57 | const createPatchedManifestMeta = (meta: any, manifest: any) => ({ 58 | ...meta, 59 | manifest: { 60 | ...meta.manifest, 61 | ...manifest, 62 | }, 63 | }); 64 | 65 | const newVersionMeta = createPatchedManifestMeta(oldMeta, { version: '2.4.0' }); 66 | const newAndroidMeta = createPatchedManifestMeta(newVersionMeta, { android: { versionCode: 13 } }); 67 | const newIosMeta = createPatchedManifestMeta(newAndroidMeta, { ios: { buildNumber: '2.4.0' } }); 68 | 69 | getPlatforms.mockReturnValue(['android', 'ios']); 70 | bumpVersion.mockReturnValue(newVersionMeta.manifest); 71 | bumpPlatformAndroid.mockReturnValue(newAndroidMeta.manifest); 72 | bumpPlatformIos.mockReturnValue(newIosMeta.manifest); 73 | 74 | const received = bumpAllVersion(oldMeta, config, context); 75 | 76 | expect(received).toBe(newIosMeta.manifest); 77 | expect(bumpVersion).toBeCalledWith(oldMeta, config, context); 78 | expect(bumpPlatformAndroid).toBeCalledWith(newVersionMeta, config, context); 79 | expect(bumpPlatformIos).toBeCalledWith(newAndroidMeta, config, context); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /src/expo.ts: -------------------------------------------------------------------------------- 1 | import detectIndent from 'detect-indent'; 2 | import detectNewline from 'detect-newline'; 3 | import { readFile, writeJson } from 'fs-extra'; 4 | import { Context } from './types'; 5 | 6 | /** 7 | * A manifest-light definition to use for typehinting. 8 | * 9 | * @todo Replace with `@types/expo` when react-native and node console issue is resolved 10 | */ 11 | export interface Manifest { 12 | name: string; 13 | version?: string; 14 | platforms?: string[]; 15 | android?: { versionCode: string | number }; 16 | ios?: { buildNumber: string }; 17 | [propName: string]: any; 18 | } 19 | 20 | /** 21 | * A small manifest description object to include filenames. 22 | * This is required when reading mulitple manifests to keep track of the filenames. 23 | */ 24 | export interface ManifestMeta { 25 | filename: string; 26 | content: string; 27 | manifest: Manifest; 28 | } 29 | 30 | /** 31 | * The name of the default Expo manifest file. 32 | */ 33 | export const MANIFEST_FILE = 'app.json'; 34 | 35 | /** 36 | * The default indentation to use when no indentation is found. 37 | */ 38 | export const DEFAULT_INDENT = ' '; 39 | 40 | /** 41 | * The default newline character to use when no existing was detected. 42 | */ 43 | export const DEFAULT_NEWLINE = '\n'; 44 | 45 | /** 46 | * Log information about the manifest which is related to the error. 47 | */ 48 | export function logManifestFromError(context: Context, error: any) { 49 | if (error && error.expo) { 50 | context.logger.log('Error encountered for %s manifest %s', 'Expo', error.expo); 51 | } 52 | } 53 | 54 | /** 55 | * Read the Expo manifest content and return the parsed JSON. 56 | */ 57 | export async function readManifest(filename: string): Promise { 58 | try { 59 | const content = await readFile(filename, 'utf8'); 60 | const manifest = JSON.parse(content).expo; 61 | 62 | return { filename, content, manifest }; 63 | } catch (error) { 64 | error.expo = filename; 65 | throw error; 66 | } 67 | } 68 | 69 | /** 70 | * Read a list of Expo mannifest files and return the parsed JSON. 71 | */ 72 | export async function readManifests(filenames: string[]): Promise { 73 | return await Promise.all(filenames.map(readManifest)); 74 | } 75 | 76 | /** 77 | * Write new content to the Expo manifest file, keeping indentation intact. 78 | */ 79 | export async function writeManifest(meta: ManifestMeta, manifest: Manifest) { 80 | const { indent } = detectIndent(meta.content) || { indent: DEFAULT_INDENT }; 81 | const newline = detectNewline(meta.content) || DEFAULT_NEWLINE; 82 | 83 | await writeJson(meta.filename, { expo: manifest }, { spaces: indent, EOL: newline }); 84 | } 85 | 86 | /** 87 | * Get the platforms from a loaded manifest. 88 | * This will fallback to the default from Expo itself. 89 | */ 90 | export function getPlatforms(manifest: Manifest) { 91 | return manifest.platforms || ['android', 'ios']; 92 | } 93 | 94 | /** 95 | * Get the platform settings for Android, if available. 96 | */ 97 | export function getAndroidPlatform(manifest: Manifest) { 98 | return manifest.android || { versionCode: 0 }; 99 | } 100 | 101 | /** 102 | * Get the platform settings for iOS, if available. 103 | */ 104 | export function getIosPlatform(manifest: Manifest) { 105 | return manifest.ios || { buildNumber: '' }; 106 | } 107 | -------------------------------------------------------------------------------- /test/config.test.ts: -------------------------------------------------------------------------------- 1 | import { getManifestFiles, getPrepareConfig, getVersionTemplates, inheritPrepareConfig } from '../src/config'; 2 | import { MANIFEST_FILE } from '../src/expo'; 3 | import { createContextLogger, createContextOptions } from './factory'; 4 | 5 | describe('config', () => { 6 | describe('#getManifestFiles', () => { 7 | it('returns default manifest name when no config is set', () => { 8 | expect(getManifestFiles({})).toEqual(expect.arrayContaining([MANIFEST_FILE])); 9 | }); 10 | 11 | it('returns default manifest name when empty manifest config is set', () => { 12 | expect(getManifestFiles({ manifests: [] })).toEqual(expect.arrayContaining([MANIFEST_FILE])); 13 | }); 14 | 15 | it('returns manifests configuration', () => { 16 | const manifests = ['app.staging.json', 'app.production.json']; 17 | 18 | expect(getManifestFiles({ manifests })).toEqual(expect.arrayContaining(manifests)); 19 | }); 20 | }); 21 | 22 | describe('#getVersionTemplates', () => { 23 | it('returns recommended templates by default', () => { 24 | expect(getVersionTemplates()).toMatchObject({ 25 | android: '${recommended}', 26 | ios: '${recommended}', 27 | version: '${recommended}', 28 | }); 29 | }); 30 | 31 | it('uses template string for all templates', () => { 32 | expect(getVersionTemplates({ versions: '${code}' })).toMatchObject({ 33 | android: '${code}', 34 | ios: '${code}', 35 | version: '${code}', 36 | }); 37 | }); 38 | 39 | it('uses independent strings for all templates', () => { 40 | const versions = { 41 | android: '${code}', 42 | ios: '${next.raw}', 43 | version: '${next.raw}', 44 | }; 45 | 46 | expect(getVersionTemplates({ versions })).toMatchObject(versions); 47 | }); 48 | }); 49 | 50 | const createContextWithPrepare = (prepare: any) => ({ 51 | ...createContextLogger(), 52 | options: { 53 | ...createContextOptions().options, 54 | prepare, 55 | }, 56 | }); 57 | 58 | describe('#getPrepareConfig', () => { 59 | it('returns nothing when prepare configuration is not defined', () => { 60 | const contextWithoutPrepare = createContextWithPrepare(undefined); 61 | const contextWithSinglePrepare = createContextWithPrepare({ path: '@semantic-release/npm' }); 62 | const contextWithPrepare = createContextWithPrepare([ 63 | { path: '@semantic-release/changelog' }, 64 | { path: '@semantic-release/npm' }, 65 | ]); 66 | 67 | expect(getPrepareConfig(contextWithoutPrepare)).toBeUndefined(); 68 | expect(getPrepareConfig(contextWithSinglePrepare)).toBeUndefined(); 69 | expect(getPrepareConfig(contextWithPrepare)).toBeUndefined(); 70 | }); 71 | 72 | it('returns prepare configuration from context if defined', () => { 73 | const manifests = ['app.production.json', 'app.staging.json']; 74 | const context = createContextWithPrepare([ 75 | { path: '@semantic-release/changelog' }, 76 | { path: '@semantic-release/npm' }, 77 | { path: 'semantic-release-expo', manifests }, 78 | ]); 79 | 80 | expect(getPrepareConfig(context)).toMatchObject({ manifests }); 81 | }); 82 | }); 83 | 84 | describe('#inheritPrepareConfig', () => { 85 | it('returns verify conditions configuration if defined', () => { 86 | const config = { manifests: ['app.json'] }; 87 | const context = createContextWithPrepare([ 88 | { path: '@semantic-release/changelog' }, 89 | { path: '@semantic-release/npm' }, 90 | { 91 | manifests: ['app.production.json', 'app.staging.json'], 92 | path: 'semantic-release-expo', 93 | }, 94 | ]); 95 | 96 | expect(inheritPrepareConfig(config, context)).toMatchObject(config); 97 | }); 98 | 99 | it('returns new configuration when prepare is defined and verify conditions is not', () => { 100 | const config = {}; 101 | const manifests = ['app.production.json', 'app.staging.json']; 102 | const context = createContextWithPrepare([ 103 | { path: '@semantic-release/changelog' }, 104 | { path: '@semantic-release/npm' }, 105 | { path: 'semantic-release-expo', manifests }, 106 | ]); 107 | 108 | expect(inheritPrepareConfig(config, context)).toMatchObject({ manifests }); 109 | }); 110 | 111 | it('returns empty configuration when both prepare and verify conditions are not defined', () => { 112 | const config = {}; 113 | const context = createContextWithPrepare([ 114 | { path: '@semantic-release/changelog' }, 115 | { path: '@semantic-release/npm' }, 116 | ]); 117 | 118 | expect(inheritPrepareConfig(config, context)).toMatchObject({}); 119 | }); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /test/expo.test.ts: -------------------------------------------------------------------------------- 1 | const readFile = jest.fn(); 2 | const readJson = jest.fn(); 3 | const writeJson = jest.fn(); 4 | const detectIndent = jest.fn(); 5 | const detectNewline = jest.fn(); 6 | 7 | jest.doMock('fs-extra', () => ({ readFile, readJson, writeJson })); 8 | jest.doMock('detect-indent', () => detectIndent); 9 | jest.doMock('detect-newline', () => detectNewline); 10 | 11 | import { 12 | DEFAULT_INDENT, 13 | DEFAULT_NEWLINE, 14 | getAndroidPlatform, 15 | getIosPlatform, 16 | getPlatforms, 17 | logManifestFromError, 18 | MANIFEST_FILE, 19 | readManifest, 20 | readManifests, 21 | writeManifest, 22 | } from '../src/expo'; 23 | 24 | import { createContext } from './factory'; 25 | 26 | describe('expo', () => { 27 | describe('constants', () => { 28 | it('has correct manifest file name', () => expect(MANIFEST_FILE).toBe('app.json')); 29 | it('has double spaces as default indent', () => expect(DEFAULT_INDENT).toBe(' ')); 30 | it('has line feed as default new line', () => expect(DEFAULT_NEWLINE).toBe('\n')); 31 | }); 32 | 33 | describe('#logManifestFromError', () => { 34 | it('does not log anything for normal errors', () => { 35 | const context = createContext(); 36 | 37 | logManifestFromError(context, new Error()); 38 | 39 | expect((context.logger.log as jest.Mock).mock.calls).toHaveLength(0); 40 | }); 41 | 42 | it('does log for errors related to manifest errors', () => { 43 | const context = createContext(); 44 | const error = new Error() as any; 45 | error.expo = 'app.production.json'; 46 | 47 | logManifestFromError(context, error); 48 | 49 | expect(context.logger.log).toBeCalledWith( 50 | 'Error encountered for %s manifest %s', 51 | 'Expo', 52 | 'app.production.json', 53 | ); 54 | }); 55 | }); 56 | 57 | describe('#readManifest', () => { 58 | it('reads the manifest file', async () => { 59 | readFile.mockResolvedValue('{ "expo": { "name": "test" } }'); 60 | 61 | const meta = await readManifest(MANIFEST_FILE); 62 | 63 | expect(readFile).toBeCalledWith(MANIFEST_FILE, 'utf8'); 64 | expect(meta.manifest).toMatchObject({ name: 'test' }); 65 | }); 66 | }); 67 | 68 | describe('#readManifests', () => { 69 | it('reads multiple manifest files', async () => { 70 | readFile 71 | .mockResolvedValueOnce('{ "expo": { "name": "first" } }') 72 | .mockResolvedValueOnce('{ "expo": { "name": "second" } }'); 73 | 74 | const metas = await readManifests([MANIFEST_FILE, MANIFEST_FILE]); 75 | 76 | expect(readFile).toHaveBeenNthCalledWith(1, MANIFEST_FILE, 'utf8'); 77 | expect(readFile).toHaveBeenNthCalledWith(2, MANIFEST_FILE, 'utf8'); 78 | 79 | expect(metas[0].manifest).toMatchObject({ name: 'first' }); 80 | expect(metas[1].manifest).toMatchObject({ name: 'second' }); 81 | }); 82 | }); 83 | 84 | describe('#writeManifest', () => { 85 | it('writes the manifest file with indentation detection', async () => { 86 | const manifestData = { name: 'test' }; 87 | const manifestString = `{ 88 | "expo": { 89 | "name": "old" 90 | } 91 | }`; 92 | 93 | const manifestMeta = { 94 | content: manifestString, 95 | filename: MANIFEST_FILE, 96 | manifest: JSON.parse(manifestString).expo, 97 | }; 98 | 99 | detectIndent.mockReturnValue({ indent: '\t' }); 100 | detectNewline.mockReturnValue('\n'); 101 | 102 | await writeManifest(manifestMeta, manifestData); 103 | 104 | expect(detectIndent).toBeCalledWith(manifestString); 105 | expect(detectNewline).toBeCalledWith(manifestString); 106 | expect(writeJson).toBeCalledWith(MANIFEST_FILE, { expo: manifestData }, { spaces: '\t', EOL: '\n' }); 107 | }); 108 | 109 | it('writes manifest file with fallback indentation', async () => { 110 | const manifestData = { name: 'test' }; 111 | const manifestString = `{ 112 | "expo": { 113 | "name": "old" 114 | } 115 | }`; 116 | 117 | const manifestMeta = { 118 | content: manifestString, 119 | filename: MANIFEST_FILE, 120 | manifest: JSON.parse(manifestString).expo, 121 | }; 122 | 123 | const options = { 124 | EOL: DEFAULT_NEWLINE, 125 | spaces: DEFAULT_INDENT, 126 | }; 127 | 128 | detectIndent.mockReturnValue(undefined); 129 | detectNewline.mockReturnValue(undefined); 130 | 131 | await writeManifest(manifestMeta, manifestData); 132 | 133 | expect(detectIndent).toBeCalledWith(manifestString); 134 | expect(detectNewline).toBeCalledWith(manifestString); 135 | expect(writeJson).toBeCalledWith(MANIFEST_FILE, { expo: manifestData }, options); 136 | }); 137 | }); 138 | 139 | describe('#getPlatforms', () => { 140 | it('returns default platforms', () => { 141 | const platforms = getPlatforms({ name: 'test' }); 142 | 143 | expect(platforms).toContain('android'); 144 | expect(platforms).toContain('ios'); 145 | }); 146 | 147 | it('returns defined platforms from manifest', () => { 148 | const platforms = ['android']; 149 | 150 | expect(getPlatforms({ name: 'test', platforms })).toBe(platforms); 151 | }); 152 | }); 153 | 154 | describe('#getAndroidPlatform', () => { 155 | it('returns default android settings', () => { 156 | expect(getAndroidPlatform({ name: 'test' })).toMatchObject({ versionCode: 0 }); 157 | }); 158 | 159 | it('returns defined android settings from manifest', () => { 160 | const android = { versionCode: 1337 }; 161 | 162 | expect(getAndroidPlatform({ name: 'test', android })).toMatchObject(android); 163 | }); 164 | }); 165 | 166 | describe('#getIosPlatform', () => { 167 | it('returns default ios settings', () => { 168 | expect(getIosPlatform({ name: 'test' })).toMatchObject({ buildNumber: '' }); 169 | }); 170 | 171 | it('returns defined ios settings from manifest', () => { 172 | const ios = { buildNumber: '1.3.7' }; 173 | 174 | expect(getIosPlatform({ name: 'test', ios })).toMatchObject(ios); 175 | }); 176 | }); 177 | }); 178 | -------------------------------------------------------------------------------- /test/version.test.ts: -------------------------------------------------------------------------------- 1 | const lodashTemplate = jest.fn(); 2 | const getVersionTemplates = jest.fn(); 3 | const getAndroidPlatform = jest.fn(); 4 | const getIosPlatform = jest.fn(); 5 | 6 | jest.doMock('lodash', () => ({ template: lodashTemplate })); 7 | jest.doMock('../src/config', () => ({ getVersionTemplates })); 8 | jest.doMock('../src/expo', () => ({ getAndroidPlatform, getIosPlatform })); 9 | 10 | import { coerce } from 'semver'; 11 | import { 12 | calculateAndroidVersion, 13 | calculateIosVersion, 14 | calculateVersion, 15 | getDefaultVariables, 16 | getVersionCode, 17 | } from '../src/version'; 18 | 19 | import { createContext, createManifestMeta } from './factory'; 20 | 21 | describe('version', () => { 22 | describe('#getVersionCode', () => { 23 | it('calculates new version with expo and next release', () => { 24 | expect(getVersionCode(coerce('1.5.9')!, coerce('29.0.1')!)).toBe(290010509); 25 | expect(getVersionCode(coerce('0.2.1')!, coerce('25.4.0')!)).toBe(250000201); 26 | expect(getVersionCode(coerce('4.10.20')!, coerce('30.0.0')!)).toBe(300041020); 27 | expect(getVersionCode(coerce('10.20.30')!, coerce('27.10.30')!)).toBe(270102030); 28 | }); 29 | }); 30 | 31 | describe('#getDefaultVariables', () => { 32 | it('returns expo version, last and next release, recommended version and (numeric) version code', () => { 33 | const meta = createManifestMeta({ name: 'test-app', sdkVersion: '29.0.1' }); 34 | const context = createContext({ 35 | last: { 36 | gitHead: '192aqs', 37 | gitTag: 'v2.5.12', 38 | version: '2.5.12', 39 | }, 40 | next: { 41 | gitHead: '271abq', 42 | gitTag: 'v3.0.0', 43 | notes: 'New major release', 44 | version: '3.0.0', 45 | }, 46 | }); 47 | 48 | expect(getDefaultVariables(meta, context)).toMatchObject({ 49 | code: 290030000, 50 | expo: coerce('29.0.1'), 51 | last: coerce('2.5.12'), 52 | next: coerce('3.0.0'), 53 | recommended: '3.0.0', 54 | }); 55 | }); 56 | }); 57 | 58 | const sharedConfig = {}; 59 | const sharedMeta = createManifestMeta({ name: 'test-app', sdkVersion: '29.1.0' }); 60 | const sharedContext = createContext({ 61 | last: { 62 | gitHead: '12j1ad', 63 | gitTag: 'v4.5.1', 64 | version: '4.5.1', 65 | }, 66 | next: { 67 | gitHead: 'kl1dsq', 68 | gitTag: 'v4.6.0', 69 | notes: 'New minor release', 70 | version: '4.6.0', 71 | }, 72 | }); 73 | 74 | const sharedVariables = { 75 | code: 290040600, 76 | expo: coerce('29.1.0'), 77 | last: coerce('4.5.1'), 78 | next: coerce('4.6.0'), 79 | recommended: '4.6.0', 80 | }; 81 | 82 | describe('#calculateVersion', () => { 83 | it('returns new version using template', () => { 84 | const templateCompiler = jest.fn(); 85 | 86 | getVersionTemplates.mockReturnValue({ version: '${next.raw}' }); 87 | lodashTemplate.mockReturnValue(templateCompiler); 88 | templateCompiler.mockReturnValue('4.6.0'); 89 | 90 | expect(calculateVersion(sharedMeta, sharedConfig, sharedContext)).toMatch('4.6.0'); 91 | expect(lodashTemplate).toBeCalledWith('${next.raw}'); 92 | expect(templateCompiler).toBeCalledWith({ ...sharedVariables, increment: 1 }); 93 | }); 94 | 95 | it('returns proper incremental versions', () => { 96 | const templateCompiler = jest.fn(); 97 | const meta = createManifestMeta({ 98 | name: 'test-app', 99 | sdkVersion: '28.0.0', 100 | version: '8', 101 | }); 102 | 103 | getVersionTemplates.mockReturnValue({ version: '${increment}' }); 104 | lodashTemplate.mockReturnValue(templateCompiler); 105 | templateCompiler.mockReturnValue('9'); 106 | 107 | expect(calculateVersion(meta, sharedConfig, sharedContext)).toMatch('9'); 108 | expect(lodashTemplate).toBeCalledWith('${increment}'); 109 | expect(templateCompiler).toBeCalledWith({ 110 | ...sharedVariables, 111 | code: 280040600, 112 | expo: coerce('28.0.0'), 113 | increment: 9, 114 | }); 115 | }); 116 | }); 117 | 118 | describe('#calculateAndroidVersion', () => { 119 | it('returns new version using template', () => { 120 | const templateCompiler = jest.fn(); 121 | 122 | getVersionTemplates.mockReturnValue({ android: '${code}' }); 123 | getAndroidPlatform.mockReturnValue({ versionCode: '290040501' }); 124 | lodashTemplate.mockReturnValue(templateCompiler); 125 | templateCompiler.mockReturnValue('290040600'); 126 | 127 | expect(calculateAndroidVersion(sharedMeta, sharedConfig, sharedContext)).toMatch('290040600'); 128 | expect(lodashTemplate).toBeCalledWith('${code}'); 129 | expect(templateCompiler).toBeCalledWith({ 130 | ...sharedVariables, 131 | increment: 290040502, 132 | recommended: sharedVariables.code, 133 | }); 134 | }); 135 | }); 136 | 137 | describe('#calculateIosVersion', () => { 138 | it('returns new version using template', () => { 139 | const templateCompiler = jest.fn(); 140 | 141 | getVersionTemplates.mockReturnValue({ ios: '${recommended}' }); 142 | getIosPlatform.mockReturnValue({ buildNumber: '4.5.1' }); 143 | lodashTemplate.mockReturnValue(templateCompiler); 144 | templateCompiler.mockReturnValue('4.6.0'); 145 | 146 | expect(calculateIosVersion(sharedMeta, sharedConfig, sharedContext)).toMatch('4.6.0'); 147 | expect(lodashTemplate).toBeCalledWith('${recommended}'); 148 | expect(templateCompiler).toBeCalledWith({ ...sharedVariables, increment: 1 }); 149 | }); 150 | 151 | it('returns proper incremental versions', () => { 152 | const templateCompiler = jest.fn(); 153 | const meta = createManifestMeta({ 154 | name: 'test-app', 155 | sdkVersion: '28.0.0', 156 | version: '8', 157 | }); 158 | 159 | getVersionTemplates.mockReturnValue({ ios: '${increment}' }); 160 | getIosPlatform.mockReturnValue({ buildNumber: '8' }); 161 | lodashTemplate.mockReturnValue(templateCompiler); 162 | templateCompiler.mockReturnValue('9'); 163 | 164 | expect(calculateIosVersion(meta, sharedConfig, sharedContext)).toMatch('9'); 165 | expect(lodashTemplate).toBeCalledWith('${increment}'); 166 | expect(templateCompiler).toBeCalledWith({ 167 | ...sharedVariables, 168 | code: 280040600, 169 | expo: coerce('28.0.0'), 170 | increment: 9, 171 | }); 172 | }); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /docs/terminal.cast: -------------------------------------------------------------------------------- 1 | {"version": 2, "width": 152, "height": 39, "timestamp": 1529190158, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} 2 | [0.195449, "o", "\u001b]7;file://Cedrics-MacBook-Pro.local/Users/cedric/Projects/cool-stuff/expo\u0007"] 3 | [0.604879, "o", "\u001b[1m\u001b[7m\u001b[27m\u001b[1m\u001b[0m "] 4 | [0.605634, "o", "\u001b]2;cedric@Cedrics-MacBook-Pro\u0007\u001b]1;..ol-stuff/expo\u0007"] 5 | [0.609734, "o", "\u001b]7;file://Cedrics-MacBook-Pro.local/Users/cedric/Projects/cool-stuff/expo\u0007"] 6 | [0.706846, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[31mcedric\u001b[00m\u001b[37m at \u001b[33mCedrics-MacBook-Pro\u001b[00m \u001b[37min \u001b[32m~/Projects/cool-stuff/expo\u001b[00m \u001b[37mon \u001b[34mdevelop \u001b[31m✗\u001b[00m\u001b[00m \r\n\u001b[37m>\u001b[00m "] 7 | [1.689437, "o", "n"] 8 | [2.033575, "o", "\bnp"] 9 | [2.333689, "o", "x"] 10 | [2.898573, "o", " "] 11 | [3.086607, "o", "s"] 12 | [3.243459, "o", "e"] 13 | [3.363448, "o", "m"] 14 | [3.463563, "o", "a"] 15 | [3.617522, "o", "n"] 16 | [3.868496, "o", "t"] 17 | [3.998425, "o", "i"] 18 | [4.162681, "o", "c"] 19 | [4.405658, "o", "-"] 20 | [4.524444, "o", "r"] 21 | [4.650499, "o", "e"] 22 | [4.742452, "o", "l"] 23 | [4.846777, "o", "e"] 24 | [5.015451, "o", "a"] 25 | [5.162458, "o", "s"] 26 | [5.369437, "o", "e"] 27 | [5.968867, "o", "\u001b[?1l\u001b>"] 28 | [5.969111, "o", "\u001b[?2004l\r\r\n"] 29 | [5.970733, "o", "\u001b]2;npx semantic-release\u0007\u001b]1;npx\u0007"] 30 | [6.453498, "o", "[Semantic release]: Running semantic-release version 15.5.2\r\n"] 31 | [6.478494, "o", "[Semantic release]: Load plugin \"verifyConditions\" from @semantic-release/changelog\r\n"] 32 | [6.518253, "o", "[Semantic release]: Load plugin \"verifyConditions\" from semantic-release-expo\r\n"] 33 | [6.5512, "o", "[Semantic release]: Load plugin \"verifyConditions\" from semantic-release-git-branches\r\n"] 34 | [6.72538, "o", "[Semantic release]: Load plugin \"analyzeCommits\" from @semantic-release/commit-analyzer\r\n"] 35 | [6.745416, "o", "[Semantic release]: Load plugin \"generateNotes\" from @semantic-release/release-notes-generator\r\n"] 36 | [6.809764, "o", "[Semantic release]: Load plugin \"prepare\" from @semantic-release/changelog\r\n"] 37 | [6.810335, "o", "[Semantic release]: Load plugin \"prepare\" from semantic-release-expo\r\n"] 38 | [6.810819, "o", "[Semantic release]: Load plugin \"prepare\" from semantic-release-git-branches\r\n"] 39 | [6.811755, "o", "[Semantic release]: Load plugin \"publish\" from @semantic-release/exec\r\n"] 40 | [10.204863, "o", "[Semantic release]: Run automated release from branch develop\r\n"] 41 | [10.205221, "o", "[Semantic release]: Call plugin verify-conditions\r\n"] 42 | [10.209046, "o", "[Semantic release]: Found Expo manifest for Cool Stuff\r\n"] 43 | [13.625448, "o", "[Semantic release]: Found git tag 0.4.0 associated with version 0.4.0\r\n"] 44 | [13.6589, "o", "[Semantic release]: Found 1 commits since last release\r\n"] 45 | [13.659155, "o", "[Semantic release]: Call plugin analyze-commits\r\n"] 46 | [13.683359, "o", "[Semantic release]: Analyzing commit: feature: my cool new feature\r\n"] 47 | [13.685013, "o", "[Semantic release]: The release type for the commit is minor\r\n"] 48 | [13.685766, "o", "[Semantic release]: Analysis of 1 commits complete: minor release\r\n"] 49 | [13.686559, "o", "[Semantic release]: The next release version is 0.5.0\r\n"] 50 | [13.699287, "o", "[Semantic release]: Call plugin verify-release\r\n"] 51 | [13.700428, "o", "[Semantic release]: Call plugin generateNotes\r\n"] 52 | [13.795898, "o", "[Semantic release]: Call plugin prepare\r\n"] 53 | [13.805727, "o", "[Semantic release]: Update CHANGELOG.md\r\n"] 54 | [13.823613, "o", "[Semantic release]: Expo manifest version changed (0.4.0 => 0.5.0)\r\n"] 55 | [13.82428, "o", "[Semantic release]: Expo manifest android version changed (9 => 10)\r\n"] 56 | [13.825155, "o", "[Semantic release]: Expo manifest ios version changed (0.4.0 => 0.5.0)\r\n"] 57 | [13.828635, "o", "[Semantic release]: New Expo manifest written\r\n"] 58 | [13.871786, "o", "[Semantic release]: Creating new release branch release/0.5.0\r\n"] 59 | [13.897956, "o", "[Semantic release]: Add CHANGELOG.md to the release commit\r\n"] 60 | [13.898055, "o", "[Semantic release]: Add app.json to the release commit\r\n"] 61 | [13.89831, "o", "[Semantic release]: Found 2 file(s) to commit\r\n"] 62 | [13.961857, "o", "[Semantic release]: Creating tag 0.5.0\r\n"] 63 | [13.962238, "o", "[Semantic release]: Pushing release branch release/0.5.0 to remote\r\n"] 64 | [16.146378, "o", "[Semantic release]: Pulling branch develop\r\n"] 65 | [17.947674, "o", "[Semantic release]: Merging release branch release/0.5.0 into develop\r\n"] 66 | [17.957613, "o", "[Semantic release]: Pushing updated branch develop\r\n"] 67 | [20.350604, "o", "[Semantic release]: Cleaning up for next branch...\r\n"] 68 | [20.371932, "o", "[Semantic release]: Pulling branch master\r\n"] 69 | [22.260317, "o", "[Semantic release]: Merging release branch release/0.5.0 into master\r\n"] 70 | [22.270493, "o", "[Semantic release]: Pushing updated branch master\r\n"] 71 | [25.073749, "o", "[Semantic release]: Cleaning up for next branch...\r\n"] 72 | [25.090189, "o", "[Semantic release]: Prepared Git release: 0.5.0\r\n"] 73 | [25.100732, "o", "[Semantic release]: Call plugin generateNotes\r\n"] 74 | [25.161466, "o", "[Semantic release]: Create tag 0.5.0\r\n"] 75 | [28.658774, "o", "[Semantic release]: Call plugin publish\r\n"] 76 | [28.660572, "o", "[Semantic release]: Call script echo \"done!\"\r\n"] 77 | [28.666848, "o", "done!\r\n"] 78 | [28.669226, "o", "[Semantic release]: The command echo \"done!\" wrote invalid JSON to stdout. The stdout content will be ignored.\r\n"] 79 | [28.669856, "o", "[Semantic release]: Published release: 0.5.0\r\n"] 80 | [28.678458, "o", "\u001b[1m\u001b[7m\u001b[27m\u001b[1m\u001b[0m "] 81 | [28.678747, "o", "\u001b]2;cedric@Cedrics-MacBook-Pro\u0007\u001b]1;..ol-stuff/expo\u0007"] 82 | [28.683365, "o", "\u001b]7;file://Cedrics-MacBook-Pro.local/Users/cedric/Projects/cool-stuff/expo\u0007"] 83 | [28.763184, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[31mcedric\u001b[00m\u001b[37m at \u001b[33mCedrics-MacBook-Pro\u001b[00m \u001b[37min \u001b[32m~/Projects/cool-stuff/expo\u001b[00m \u001b[37mon \u001b[34mrelease/0.5.0 \u001b[31m✗\u001b[00m\u001b[00m \r\n\u001b[37m>\u001b[00m \u001b[K\u001b[134C\u001b[00m\u001b[33m\u001b[00m \u001b[31m\u001b[00m \u001b[37m\u001b[00m\u001b[149D"] 84 | [28.763484, "o", "\u001b[?1h\u001b=\u001b[?2004h"] 85 | [31.118709, "o", "\u001b[?2004l\r\r\n"] 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Expo - Semantic Release 2 | 3 | [![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) 4 | [![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) 5 | [![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) 6 | [![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) 7 | 8 | An [Expo][expo] implementation for [semantic release][semantic-release], so you don't have to bother. 9 | 10 | ![Example](docs/terminal.svg) 11 | 12 | ## How it works 13 | 14 | Semantic release will first determine a new version based on your likings. 15 | This plugin will then search for your [Expo manifest(s)][expo-manifest] and update it accordingly. 16 | Not only will this update the [`version`][expo-version] property within the manifest. 17 | It will also update the [Android `versionCode`][expo-version-android] and [iOS `buildNumber`][expo-version-ios] platform too, based on the [configuration](#configuration). 18 | 19 | ### Verify Conditions 20 | 21 | Before such a smooth, carefree release can take place, the plugin must validate the existence of a manifest. 22 | This check takes place in the [verify condition][semantic-release-steps] step of semantic release. 23 | The name of the Expo app, defined in the [`name` property][expo-name], is dumped to console to provide some feedback for successful validation. 24 | 25 | ### Prepare 26 | 27 | This plugin writes the actual changes to the manifest during preparation. 28 | After this step, you can [publish to Expo][expo-publish], [create a new build][expo-build] or add the changes in a [release commit][semantic-release-commit]. 29 | The [`version` property][expo-version] within the manifest is always updated. 30 | All of the platform specific changes are only applied when the platform is enabled. 31 | 32 | > It is highly recommended to add the Expo manifest (`app.json`) to the [list of assets][semantic-release-assets] to include in the release commit. 33 | 34 | ## Usage 35 | 36 | Here is an example configuration with automated changelogs, `package.json` versions, Expo and git release commits. 37 | 38 | ```json 39 | { 40 | "verifyConditions": [ 41 | "semantic-release-expo", 42 | "@semantic-release/changelog", 43 | "@semantic-release/git", 44 | "@semantic-release/npm" 45 | ], 46 | "prepare": [ 47 | "semantic-release-expo", 48 | "@semantic-release/changelog", 49 | "@semantic-release/npm", 50 | { 51 | "path": "@semantic-release/git", 52 | "assets": [ 53 | "CHANGELOG.md", 54 | "package.json", 55 | "package-lock.json", 56 | "app.json" 57 | ] 58 | } 59 | ], 60 | "publish": false, 61 | "success": false, 62 | "fail": false 63 | } 64 | ``` 65 | 66 | ## Configuration 67 | 68 | By default this plugin uses configuration that should work straight out of the box. 69 | Unfortunately, all apps are different and sometimes requires a specific release flow. 70 | To satisfy these needs, you can customize some of these settings below. 71 | 72 | ### Multiple manifests 73 | 74 | Normally, an Expo app should have a single manifest located at `/app.json`. 75 | But you might have good reasons to use non-standard or multiple manifests. 76 | For example, if you need to [create multiple versions/flavours and allow then to work side-by-side][info-multiple-manifests], you need multiple manifests. 77 | To configure this plugin, you can provide a list of manifests to update. 78 | 79 | ```json 80 | { 81 | "prepare": [ 82 | { 83 | "path": "semantic-release-expo", 84 | "manifests": [ 85 | "app.test.json", 86 | "app.staging.json", 87 | "app.production.json", 88 | ] 89 | } 90 | ] 91 | } 92 | ``` 93 | 94 | > `manifests` accepts either a single string, or a list of strings. 95 | 96 | ### Version templates 97 | 98 | Unfortunately, right now there is no "universal" versioning which can be used across all platforms. 99 | For exmaple, iOS can simply use the exact semantic version (e.g. `2.5.1`) but [Android can't][info-android-semver]. 100 | To allow multiple "tactics" or personal favorites, you can change the so called "versioning templates". 101 | These templates uses [lodash template][lodash-template] to build new versions. 102 | Every version string, `version`, `Android versionCode` and `iOS buildNumber` can be modified independently. 103 | 104 | ```json 105 | { 106 | "prepare": [ 107 | { 108 | "path": "semantic-release-expo", 109 | "versions": { 110 | "version": "${next.raw}", 111 | "android": "${code}", 112 | "ios": "${next.raw}" 113 | } 114 | } 115 | ] 116 | } 117 | ``` 118 | 119 | > `versions` accepts either a single string for all versions, or a (partial) object with templates. By default the `${recommended}` template is used. 120 | 121 | #### Version templates variables 122 | 123 | Currently the following variables are available within the templates. 124 | 125 | name | type | description 126 | --- | --- | --- 127 | expo | [`SemVer`][info-semver] | The semver-coerced Expo SDK version 128 | last | [`SemVer`][info-semver] | The semver-coerced last release version 129 | next | [`SemVer`][info-semver] | The semver coerced next release version 130 | code | `Number` | The (Android) version code, using the [versioning approach by Maxi Rosson][info-android-versioncode] 131 | increment | `Number` | An incremented number of the previous version, [discouraged because of non-deterministic behaviour][repo-issue-increments]. 132 | recommended | `String` or `Number` | _differs per versioning/platform, listed below_ 133 | 134 | ##### Recommended per version type 135 | 136 | version | example | description 137 | --- | --- | --- 138 | [version][expo-version] | `1.2.3` | The "raw" next release version (also available in `${next.raw}`) 139 | [Android versionCode][expo-version-android] | `290010203` | The [versioning approach by Maxi Rosson][info-android-versioncode] (same as `${code}`) 140 | [iOS buildNumber][expo-version-ios] | `1.2.3` | The "raw" next release version (also available in `${next.raw}`) 141 | 142 | > In these examples Expo SDK `29.x.x` and SemVer `1.2.3` is used. 143 | 144 | ## License 145 | 146 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 147 | 148 | [expo]: https://expo.io/ 149 | [expo-build]: https://docs.expo.io/versions/latest/distribution/building-standalone-apps 150 | [expo-manifest]: https://docs.expo.io/versions/latest/workflow/configuration 151 | [expo-name]: https://docs.expo.io/versions/latest/workflow/configuration#name 152 | [expo-publish]: https://docs.expo.io/versions/latest/workflow/publishing 153 | [expo-version]: https://docs.expo.io/versions/latest/workflow/configuration#version 154 | [expo-version-android]: https://docs.expo.io/versions/latest/workflow/configuration#android 155 | [expo-version-ios]: https://docs.expo.io/versions/latest/workflow/configuration#ios 156 | [semantic-release]: https://github.com/semantic-release/semantic-release 157 | [semantic-release-assets]: https://github.com/semantic-release/git#assets 158 | [semantic-release-commit]: https://github.com/semantic-release/git#prepare 159 | [semantic-release-steps]: https://github.com/semantic-release/semantic-release#release-steps 160 | [lodash-template]: https://www.npmjs.com/package/lodash.template 161 | [info-multiple-manifests]: https://blog.expo.io/setting-up-expo-and-bitbucket-pipelines-8995ef036a18#e09a 162 | [info-android-semver]: https://github.com/semver/semver/issues/309 163 | [info-android-versioncode]: https://medium.com/@maxirosson/versioning-android-apps-d6ec171cfd82 164 | [info-semver]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/257c8bc48a08620b77088af209ff3f4153221784/types/semver/index.d.ts#L152-L171 165 | [repo-issue-increments]: https://github.com/byCedric/semantic-release-expo/issues/4#issuecomment-417583573 166 | 167 | --- --- 168 | 169 |

170 | with :heart: byCedric & Contributors 171 |

172 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.2.3](https://github.com/bycedric/semantic-release-expo/compare/2.2.2...2.2.3) (2019-10-18) 2 | 3 | 4 | ### Other chores 5 | 6 | * **deps:** bump eslint-utils from 1.4.0 to 1.4.2 ([#144](https://github.com/bycedric/semantic-release-expo/issues/144)) ([ace8f9d](https://github.com/bycedric/semantic-release-expo/commit/ace8f9d)) 7 | * **deps-dev:** bump [@commitlint](https://github.com/commitlint)/travis-cli from 8.1.0 to 8.2.0 ([#157](https://github.com/bycedric/semantic-release-expo/issues/157)) ([378a84c](https://github.com/bycedric/semantic-release-expo/commit/378a84c)) 8 | * **deps-dev:** bump [@types](https://github.com/types)/detect-indent from 5.0.0 to 6.0.0 ([#141](https://github.com/bycedric/semantic-release-expo/issues/141)) ([822ad7e](https://github.com/bycedric/semantic-release-expo/commit/822ad7e)) 9 | * **deps-dev:** bump [@types](https://github.com/types)/jest from 24.0.15 to 24.0.16 ([#142](https://github.com/bycedric/semantic-release-expo/issues/142)) ([662cc52](https://github.com/bycedric/semantic-release-expo/commit/662cc52)) 10 | * **deps-dev:** bump [@types](https://github.com/types)/jest from 24.0.16 to 24.0.18 ([#146](https://github.com/bycedric/semantic-release-expo/issues/146)) ([5c3f573](https://github.com/bycedric/semantic-release-expo/commit/5c3f573)) 11 | * **deps-dev:** bump [@types](https://github.com/types)/lodash from 4.14.136 to 4.14.138 ([#150](https://github.com/bycedric/semantic-release-expo/issues/150)) ([1a28183](https://github.com/bycedric/semantic-release-expo/commit/1a28183)) 12 | * **deps-dev:** bump [@types](https://github.com/types)/lodash from 4.14.138 to 4.14.141 ([#151](https://github.com/bycedric/semantic-release-expo/issues/151)) ([3cc0a40](https://github.com/bycedric/semantic-release-expo/commit/3cc0a40)) 13 | * **deps-dev:** bump [@types](https://github.com/types)/node from 12.6.8 to 12.7.3 ([#147](https://github.com/bycedric/semantic-release-expo/issues/147)) ([cb0fc4e](https://github.com/bycedric/semantic-release-expo/commit/cb0fc4e)) 14 | * **deps-dev:** bump [@types](https://github.com/types)/node from 12.7.3 to 12.7.8 ([#156](https://github.com/bycedric/semantic-release-expo/issues/156)) ([db6157e](https://github.com/bycedric/semantic-release-expo/commit/db6157e)) 15 | * **deps-dev:** bump [@types](https://github.com/types)/semver from 6.0.1 to 6.0.2 ([#153](https://github.com/bycedric/semantic-release-expo/issues/153)) ([f99ba39](https://github.com/bycedric/semantic-release-expo/commit/f99ba39)) 16 | * **deps-dev:** bump codecov from 3.5.0 to 3.6.1 ([#152](https://github.com/bycedric/semantic-release-expo/issues/152)) ([eeb49f8](https://github.com/bycedric/semantic-release-expo/commit/eeb49f8)) 17 | * **deps-dev:** bump eslint from 6.1.0 to 6.3.0 ([#145](https://github.com/bycedric/semantic-release-expo/issues/145)) ([24bb3bf](https://github.com/bycedric/semantic-release-expo/commit/24bb3bf)) 18 | * **deps-dev:** bump eslint from 6.3.0 to 6.5.1 ([#154](https://github.com/bycedric/semantic-release-expo/issues/154)) ([19f3b64](https://github.com/bycedric/semantic-release-expo/commit/19f3b64)) 19 | * **deps-dev:** bump jest from 24.8.0 to 24.9.0 ([#149](https://github.com/bycedric/semantic-release-expo/issues/149)) ([e44a74d](https://github.com/bycedric/semantic-release-expo/commit/e44a74d)) 20 | * **deps-dev:** bump ts-jest from 24.0.2 to 24.1.0 ([#158](https://github.com/bycedric/semantic-release-expo/issues/158)) ([996ef24](https://github.com/bycedric/semantic-release-expo/commit/996ef24)) 21 | * **deps-dev:** bump typescript from 3.5.3 to 3.6.2 ([#148](https://github.com/bycedric/semantic-release-expo/issues/148)) ([bdc13c7](https://github.com/bycedric/semantic-release-expo/commit/bdc13c7)) 22 | * **deps-dev:** bump typescript from 3.6.2 to 3.6.3 ([#155](https://github.com/bycedric/semantic-release-expo/issues/155)) ([cf95220](https://github.com/bycedric/semantic-release-expo/commit/cf95220)) 23 | 24 | 25 | ### Pipeline changes 26 | 27 | * only audit production dependencies ([4f8ee8a](https://github.com/bycedric/semantic-release-expo/commit/4f8ee8a)) 28 | 29 | ## [2.2.2](https://github.com/bycedric/semantic-release-expo/compare/2.2.1...2.2.2) (2019-07-27) 30 | 31 | 32 | ### Code refactors 33 | 34 | * update dependencies and node lts for releases ([#140](https://github.com/bycedric/semantic-release-expo/issues/140)) ([ba6a878](https://github.com/bycedric/semantic-release-expo/commit/ba6a878)) 35 | 36 | ## [2.2.1](https://github.com/bycedric/semantic-release-expo/compare/2.2.0...2.2.1) (2019-07-13) 37 | 38 | 39 | ### Bug fixes 40 | 41 | * only add built source files to packed tarball ([#139](https://github.com/bycedric/semantic-release-expo/issues/139)) ([5a71fe2](https://github.com/bycedric/semantic-release-expo/commit/5a71fe2)) 42 | 43 | # [2.2.0](https://github.com/bycedric/semantic-release-expo/compare/2.1.6...2.2.0) (2019-07-13) 44 | 45 | 46 | ### Code refactors 47 | 48 | * lower the dependabot pr intensity ([#122](https://github.com/bycedric/semantic-release-expo/issues/122)) ([e157739](https://github.com/bycedric/semantic-release-expo/commit/e157739)) 49 | * move types dependencies to dev dependencies ([#134](https://github.com/bycedric/semantic-release-expo/issues/134)) ([9dae0e7](https://github.com/bycedric/semantic-release-expo/commit/9dae0e7)) 50 | * update dependencies and fix all audit issues ([#133](https://github.com/bycedric/semantic-release-expo/issues/133)) ([64ca7fd](https://github.com/bycedric/semantic-release-expo/commit/64ca7fd)) 51 | 52 | 53 | ### New features 54 | 55 | * replace tslint with eslint for improved linting ([#138](https://github.com/bycedric/semantic-release-expo/issues/138)) ([fafa74a](https://github.com/bycedric/semantic-release-expo/commit/fafa74a)) 56 | 57 | 58 | ### Other chores 59 | 60 | * **deps:** [security] bump lodash from 4.17.11 to 4.17.13 ([#131](https://github.com/bycedric/semantic-release-expo/issues/131)) ([700f001](https://github.com/bycedric/semantic-release-expo/commit/700f001)) 61 | * **deps:** [security] bump lodash.template from 4.4.0 to 4.5.0 ([#130](https://github.com/bycedric/semantic-release-expo/issues/130)) ([618462a](https://github.com/bycedric/semantic-release-expo/commit/618462a)) 62 | * **deps:** bump [@types](https://github.com/types)/lodash from 4.14.123 to 4.14.134 ([#117](https://github.com/bycedric/semantic-release-expo/issues/117)) ([aac87f6](https://github.com/bycedric/semantic-release-expo/commit/aac87f6)) 63 | * **deps-dev:** bump [@commitlint](https://github.com/commitlint)/travis-cli from 7.6.1 to 8.0.0 ([#114](https://github.com/bycedric/semantic-release-expo/issues/114)) ([43df270](https://github.com/bycedric/semantic-release-expo/commit/43df270)) 64 | * **deps-dev:** bump [@types](https://github.com/types)/jest from 24.0.11 to 24.0.15 ([#121](https://github.com/bycedric/semantic-release-expo/issues/121)) ([781fcd3](https://github.com/bycedric/semantic-release-expo/commit/781fcd3)) 65 | * **deps-dev:** bump [@types](https://github.com/types)/node from 12.0.2 to 12.0.8 ([#118](https://github.com/bycedric/semantic-release-expo/issues/118)) ([240d967](https://github.com/bycedric/semantic-release-expo/commit/240d967)) 66 | * **deps-dev:** bump codecov from 3.3.0 to 3.5.0 ([#107](https://github.com/bycedric/semantic-release-expo/issues/107)) ([37f156d](https://github.com/bycedric/semantic-release-expo/commit/37f156d)) 67 | * **deps-dev:** bump tslint from 5.16.0 to 5.17.0 ([#113](https://github.com/bycedric/semantic-release-expo/issues/113)) ([f4d7a2c](https://github.com/bycedric/semantic-release-expo/commit/f4d7a2c)) 68 | * **deps-dev:** bump typescript from 3.4.5 to 3.5.2 ([#119](https://github.com/bycedric/semantic-release-expo/issues/119)) ([2b6bccf](https://github.com/bycedric/semantic-release-expo/commit/2b6bccf)) 69 | 70 | 71 | ### Pipeline changes 72 | 73 | * remove audit step to ease up on travis ([#132](https://github.com/bycedric/semantic-release-expo/issues/132)) ([4c1e74f](https://github.com/bycedric/semantic-release-expo/commit/4c1e74f)) 74 | 75 | ## [2.1.6](https://github.com/bycedric/semantic-release-expo/compare/2.1.5...2.1.6) (2019-05-26) 76 | 77 | 78 | ### Code refactors 79 | 80 | * upgrade detect-indent to version 6.0.0 ([#92](https://github.com/bycedric/semantic-release-expo/issues/92)) ([0a3f8a0](https://github.com/bycedric/semantic-release-expo/commit/0a3f8a0)) 81 | * upgrade fs-extra to version 8.0.0 ([#94](https://github.com/bycedric/semantic-release-expo/issues/94)) ([0d4460e](https://github.com/bycedric/semantic-release-expo/commit/0d4460e)) 82 | * use dependabot instead of greenkeeper ([#102](https://github.com/bycedric/semantic-release-expo/issues/102)) ([c03251a](https://github.com/bycedric/semantic-release-expo/commit/c03251a)) 83 | 84 | 85 | ### Other chores 86 | 87 | * **deps:** [security] bump tar from 2.2.1 to 2.2.2 ([#97](https://github.com/bycedric/semantic-release-expo/issues/97)) ([d901b06](https://github.com/bycedric/semantic-release-expo/commit/d901b06)) 88 | * **deps:** bump fs-extra from 8.0.0 to 8.0.1 ([#100](https://github.com/bycedric/semantic-release-expo/issues/100)) ([f3dfc16](https://github.com/bycedric/semantic-release-expo/commit/f3dfc16)) 89 | * **deps:** bump semver from 6.0.0 to 6.1.0 ([#101](https://github.com/bycedric/semantic-release-expo/issues/101)) ([eaf4348](https://github.com/bycedric/semantic-release-expo/commit/eaf4348)) 90 | * **deps-dev:** bump [@commitlint](https://github.com/commitlint)/travis-cli from 7.5.2 to 7.6.1 ([#96](https://github.com/bycedric/semantic-release-expo/issues/96)) ([b6a0f91](https://github.com/bycedric/semantic-release-expo/commit/b6a0f91)) 91 | * **deps-dev:** bump [@types](https://github.com/types)/fs-extra from 5.0.5 to 7.0.0 ([#98](https://github.com/bycedric/semantic-release-expo/issues/98)) ([baf56a9](https://github.com/bycedric/semantic-release-expo/commit/baf56a9)) 92 | * **deps-dev:** bump [@types](https://github.com/types)/node from 11.13.7 to 12.0.2 ([#99](https://github.com/bycedric/semantic-release-expo/issues/99)) ([909098e](https://github.com/bycedric/semantic-release-expo/commit/909098e)) 93 | 94 | ## [2.1.5](https://github.com/bycedric/semantic-release-expo/compare/2.1.4...2.1.5) (2019-04-25) 95 | 96 | 97 | ### Code refactors 98 | 99 | * upgrade all dependencies to the latest versions ([#91](https://github.com/bycedric/semantic-release-expo/issues/91)) ([72a17ae](https://github.com/bycedric/semantic-release-expo/commit/72a17ae)) 100 | * upgrade detect-newline to version 3.0.0 ([#83](https://github.com/bycedric/semantic-release-expo/issues/83)) ([0eee460](https://github.com/bycedric/semantic-release-expo/commit/0eee460)) 101 | 102 | 103 | ### Other chores 104 | 105 | * configure pull request titles for greenkeeper ([#90](https://github.com/bycedric/semantic-release-expo/issues/90)) ([7682b5b](https://github.com/bycedric/semantic-release-expo/commit/7682b5b)) 106 | 107 | 108 | ### Pipeline changes 109 | 110 | * add node 12 to testing languages ([#87](https://github.com/bycedric/semantic-release-expo/issues/87)) ([4fd6f3c](https://github.com/bycedric/semantic-release-expo/commit/4fd6f3c)) 111 | * remove soon-to-be outdated node 11 in favor of 12 ([#89](https://github.com/bycedric/semantic-release-expo/issues/89)) ([97e86cb](https://github.com/bycedric/semantic-release-expo/commit/97e86cb)) 112 | * temporary disable audit-block awaiting tar/node-gyp fix ([#86](https://github.com/bycedric/semantic-release-expo/issues/86)) ([9114a27](https://github.com/bycedric/semantic-release-expo/commit/9114a27)) 113 | 114 | ## [2.1.4](https://github.com/bycedric/semantic-release-expo/compare/2.1.3...2.1.4) (2019-04-09) 115 | 116 | 117 | ### Bug fixes 118 | 119 | * security vulnerability in js-yaml ([799f154](https://github.com/bycedric/semantic-release-expo/commit/799f154)) 120 | 121 | 122 | ### Code refactors 123 | 124 | * upgrade [@types](https://github.com/types)/jest to version 24.0.10 ([#79](https://github.com/bycedric/semantic-release-expo/issues/79)) ([32af0d3](https://github.com/bycedric/semantic-release-expo/commit/32af0d3)) 125 | * upgrade [@types](https://github.com/types)/node to version 11.10.5 ([#77](https://github.com/bycedric/semantic-release-expo/issues/77)) ([4cfad69](https://github.com/bycedric/semantic-release-expo/commit/4cfad69)) 126 | * upgrade [@types](https://github.com/types)/semver to version 6.0.0 ([d791a4e](https://github.com/bycedric/semantic-release-expo/commit/d791a4e)) 127 | * upgrade semver to version 6.0.0 ([#75](https://github.com/bycedric/semantic-release-expo/issues/75)) ([5d6f706](https://github.com/bycedric/semantic-release-expo/commit/5d6f706)) 128 | * upgrade ts-jest to version 24.0.1 ([#78](https://github.com/bycedric/semantic-release-expo/issues/78)) ([4671afb](https://github.com/bycedric/semantic-release-expo/commit/4671afb)) 129 | 130 | 131 | ### Pipeline changes 132 | 133 | * allow greenkeeper package lock commits ([f92edcb](https://github.com/bycedric/semantic-release-expo/commit/f92edcb)) 134 | 135 | ## [2.1.3](https://github.com/bycedric/semantic-release-expo/compare/2.1.2...2.1.3) (2019-02-24) 136 | 137 | 138 | ### Code refactors 139 | 140 | * upgrade [@commitlint](https://github.com/commitlint)/travis-cli to version 7.5.1 ([#52](https://github.com/bycedric/semantic-release-expo/issues/52)) ([7b96eab](https://github.com/bycedric/semantic-release-expo/commit/7b96eab)) 141 | * upgrade [@commitlint](https://github.com/commitlint)/travis-cli to version 7.5.2 ([#57](https://github.com/bycedric/semantic-release-expo/issues/57)) ([93af488](https://github.com/bycedric/semantic-release-expo/commit/93af488)) 142 | * upgrade [@types](https://github.com/types)/fs-extra to version 5.0.5 ([#65](https://github.com/bycedric/semantic-release-expo/issues/65)) ([0b0a088](https://github.com/bycedric/semantic-release-expo/commit/0b0a088)) 143 | * upgrade [@types](https://github.com/types)/jest to version 24.0.6 ([#68](https://github.com/bycedric/semantic-release-expo/issues/68)) ([f77dcc5](https://github.com/bycedric/semantic-release-expo/commit/f77dcc5)), closes [#48](https://github.com/bycedric/semantic-release-expo/issues/48) 144 | * upgrade [@types](https://github.com/types)/lodash to version 4.14.121 ([#67](https://github.com/bycedric/semantic-release-expo/issues/67)) ([c0ff384](https://github.com/bycedric/semantic-release-expo/commit/c0ff384)) 145 | * upgrade [@types](https://github.com/types)/node to version 10.12.22 ([#51](https://github.com/bycedric/semantic-release-expo/issues/51)) ([57c4a4f](https://github.com/bycedric/semantic-release-expo/commit/57c4a4f)) 146 | * upgrade [@types](https://github.com/types)/node to version 11.9.5 ([#66](https://github.com/bycedric/semantic-release-expo/issues/66)) ([fa5d037](https://github.com/bycedric/semantic-release-expo/commit/fa5d037)), closes [#58](https://github.com/bycedric/semantic-release-expo/issues/58) 147 | * upgrade codecov to version 3.2.0 ([#64](https://github.com/bycedric/semantic-release-expo/issues/64)) ([e644b47](https://github.com/bycedric/semantic-release-expo/commit/e644b47)) 148 | * upgrade ts-jest to version 24.0.0 ([#62](https://github.com/bycedric/semantic-release-expo/issues/62)) ([89844f0](https://github.com/bycedric/semantic-release-expo/commit/89844f0)) 149 | 150 | 151 | ### Documentation changes 152 | 153 | * add missing whitespace ([#53](https://github.com/bycedric/semantic-release-expo/issues/53)) ([50e1f91](https://github.com/bycedric/semantic-release-expo/commit/50e1f91)) 154 | 155 | 156 | ### Other chores 157 | 158 | * update all dependencies and manually fix audit warning ([#63](https://github.com/bycedric/semantic-release-expo/issues/63)) ([408f539](https://github.com/bycedric/semantic-release-expo/commit/408f539)) 159 | 160 | 161 | ### Pipeline changes 162 | 163 | * add dry run of new release on develop ([#55](https://github.com/bycedric/semantic-release-expo/issues/55)) ([220cd31](https://github.com/bycedric/semantic-release-expo/commit/220cd31)) 164 | * audit before downloading dependencies with npm ci ([#54](https://github.com/bycedric/semantic-release-expo/issues/54)) ([f33f7d4](https://github.com/bycedric/semantic-release-expo/commit/f33f7d4)) 165 | 166 | ## [2.1.2](https://github.com/bycedric/semantic-release-expo/compare/2.1.1...2.1.2) (2019-01-30) 167 | 168 | 169 | ### Bug fixes 170 | 171 | * manually override allowed semantic release branch ([f94b4a1](https://github.com/bycedric/semantic-release-expo/commit/f94b4a1)) 172 | * missing commitlint configuration ([bb72167](https://github.com/bycedric/semantic-release-expo/commit/bb72167)) 173 | 174 | 175 | ### Code refactors 176 | 177 | * allow tests to run in watch mode ([d894470](https://github.com/bycedric/semantic-release-expo/commit/d894470)) 178 | * replace custom semantic release with convention package ([a4a1d47](https://github.com/bycedric/semantic-release-expo/commit/a4a1d47)) 179 | * update pipeline with audit for security notifications ([54ef137](https://github.com/bycedric/semantic-release-expo/commit/54ef137)) 180 | * upgrade typescript to 3.2.4 ([33ec439](https://github.com/bycedric/semantic-release-expo/commit/33ec439)) 181 | * use full lodash with tree-shaked import ([65e3114](https://github.com/bycedric/semantic-release-expo/commit/65e3114)) 182 | 183 | 184 | ### Other chores 185 | 186 | * upgrade peakfijn conventions packages ([86e2fd9](https://github.com/bycedric/semantic-release-expo/commit/86e2fd9)) 187 | 188 | 189 | ### Pipeline changes 190 | 191 | * audit dependencies before using them ([982699f](https://github.com/bycedric/semantic-release-expo/commit/982699f)) 192 | 193 | ## [2.1.1](https://github.com/bycedric/semantic-release-expo/compare/2.1.0...2.1.1) (2018-09-17) 194 | 195 | 196 | ### Bug fixes 197 | 198 | * force android version code to numeric value ([#43](https://github.com/bycedric/semantic-release-expo/issues/43)) ([0f7389d](https://github.com/bycedric/semantic-release-expo/commit/0f7389d)) 199 | 200 | 201 | ### Code refactors 202 | 203 | * upgrade to latest peakfijn conventions ([4b0fee3](https://github.com/bycedric/semantic-release-expo/commit/4b0fee3)) 204 | 205 | 206 | ### Documentation changes 207 | 208 | * add discussion and simple change templates ([8831ae6](https://github.com/bycedric/semantic-release-expo/commit/8831ae6)) 209 | 210 | 211 | ### Other chores 212 | 213 | * integrate tslint and fix all found issues ([#38](https://github.com/bycedric/semantic-release-expo/issues/38)) ([edb9129](https://github.com/bycedric/semantic-release-expo/commit/edb9129)) 214 | * simplify the issue and pull request templates ([#37](https://github.com/bycedric/semantic-release-expo/issues/37)) ([0f460df](https://github.com/bycedric/semantic-release-expo/commit/0f460df)) 215 | 216 | 217 | ### Pipeline changes 218 | 219 | * add missing changelog package for releases ([#44](https://github.com/bycedric/semantic-release-expo/issues/44)) ([c169a87](https://github.com/bycedric/semantic-release-expo/commit/c169a87)) 220 | 221 | # [2.1.0](https://github.com/bycedric/semantic-release-expo/compare/2.0.0...2.1.0) (2018-09-08) 222 | 223 | 224 | ### Bug fixes 225 | 226 | * exclude semantic release configuration from npm ([c7c212e](https://github.com/bycedric/semantic-release-expo/commit/c7c212e)) 227 | 228 | 229 | ### New features 230 | 231 | * add configurable version templates ([#36](https://github.com/bycedric/semantic-release-expo/issues/36)) ([5745c9d](https://github.com/bycedric/semantic-release-expo/commit/5745c9d)) 232 | 233 | # [2.0.0](https://github.com/bycedric/semantic-release-expo/compare/1.2.1...2.0.0) (2018-08-31) 234 | 235 | 236 | ### Bug fixes 237 | 238 | * breaking changes behaviour with semantic release ([3844459](https://github.com/bycedric/semantic-release-expo/commit/3844459)) 239 | * disable spammy codecov messages in pull requests ([29225be](https://github.com/bycedric/semantic-release-expo/commit/29225be)) 240 | 241 | 242 | ### Code refactors 243 | 244 | * upgrade commit-types-peakfijn to version 0.6.0 ([#32](https://github.com/bycedric/semantic-release-expo/issues/32)) ([5065b48](https://github.com/bycedric/semantic-release-expo/commit/5065b48)) 245 | * upgrade conventional-changelog-peakfijn to version 0.6.0 ([#33](https://github.com/bycedric/semantic-release-expo/issues/33)) ([adcbc8c](https://github.com/bycedric/semantic-release-expo/commit/adcbc8c)) 246 | * upgrade cz-changelog-peakfijn to version 0.6.0 ([#34](https://github.com/bycedric/semantic-release-expo/issues/34)) ([fe462c6](https://github.com/bycedric/semantic-release-expo/commit/fe462c6)) 247 | 248 | 249 | ### New features 250 | 251 | * make android version deterministic ([#35](https://github.com/bycedric/semantic-release-expo/issues/35)) ([1428f1a](https://github.com/bycedric/semantic-release-expo/commit/1428f1a)) 252 | 253 | 254 | ### BREAKING CHANGE 255 | 256 | * android incremental build number is replaced. This now uses an integer value calculated by multiple variables. 257 | 258 | https://medium.com/@maxirosson/versioning-android-apps-d6ec171cfd82 259 | 260 | ## [1.2.1](https://github.com/bycedric/semantic-release-expo/compare/1.2.0...1.2.1) (2018-08-04) 261 | 262 | 263 | ### Bug fixes 264 | 265 | * set jsdom test url for local storage support ([#29](https://github.com/bycedric/semantic-release-expo/issues/29)) ([1d2e9b6](https://github.com/bycedric/semantic-release-expo/commit/1d2e9b6)) 266 | 267 | 268 | ### Code refactors 269 | 270 | * upgrade [@semantic-release](https://github.com/semantic-release)/changelog to version 3.0.0 ([#23](https://github.com/bycedric/semantic-release-expo/issues/23)) ([fa3396a](https://github.com/bycedric/semantic-release-expo/commit/fa3396a)) 271 | * upgrade commit-types-peakfijn to version 0.5.0 ([#24](https://github.com/bycedric/semantic-release-expo/issues/24)) ([4f63ab5](https://github.com/bycedric/semantic-release-expo/commit/4f63ab5)) 272 | * upgrade conventional-changelog-peakfijn to version 0.5.0 ([#25](https://github.com/bycedric/semantic-release-expo/issues/25)) ([be7929d](https://github.com/bycedric/semantic-release-expo/commit/be7929d)) 273 | * upgrade cz-changelog-peakfijn to version 0.5.0 ([#26](https://github.com/bycedric/semantic-release-expo/issues/26)) ([a9bd560](https://github.com/bycedric/semantic-release-expo/commit/a9bd560)) 274 | * upgrade fs-extra to version 7.0.0 ([#22](https://github.com/bycedric/semantic-release-expo/issues/22)) ([f5198d1](https://github.com/bycedric/semantic-release-expo/commit/f5198d1)) 275 | * upgrade ts-jest to version 23.1.2 ([#31](https://github.com/bycedric/semantic-release-expo/issues/31)) ([fb14ece](https://github.com/bycedric/semantic-release-expo/commit/fb14ece)) 276 | * use release commit type for automated releases ([af87788](https://github.com/bycedric/semantic-release-expo/commit/af87788)) 277 | 278 | # [1.2.0](https://github.com/bycedric/semantic-release-expo/compare/1.1.1...1.2.0) (2018-07-11) 279 | 280 | 281 | ### Code refactors 282 | 283 | * increase usability and other minor changes ([#21](https://github.com/bycedric/semantic-release-expo/issues/21)) ([49e29b1](https://github.com/bycedric/semantic-release-expo/commit/49e29b1)), closes [#7](https://github.com/bycedric/semantic-release-expo/issues/7) [#7](https://github.com/bycedric/semantic-release-expo/issues/7) 284 | 285 | 286 | ### Code style changes 287 | 288 | * remove extraneous space in log statement ([cdaa1b9](https://github.com/bycedric/semantic-release-expo/commit/cdaa1b9)) 289 | 290 | 291 | ### New features 292 | 293 | * inherit configuration from prepare when verifying ([#20](https://github.com/bycedric/semantic-release-expo/issues/20)) ([729baeb](https://github.com/bycedric/semantic-release-expo/commit/729baeb)) 294 | 295 | ## [1.1.1](https://github.com/bycedric/semantic-release-expo/compare/1.1.0...1.1.1) (2018-07-10) 296 | 297 | 298 | ### Bug fixes 299 | 300 | * add missing reason of failure in exception ([3f198ed](https://github.com/bycedric/semantic-release-expo/commit/3f198ed)) 301 | * log the manifest file throwing the exception ([05183f4](https://github.com/bycedric/semantic-release-expo/commit/05183f4)) 302 | 303 | 304 | ### Code refactors 305 | 306 | * upgrade [@types](https://github.com/types)/fs-extra to version 5.0.4 ([154afed](https://github.com/bycedric/semantic-release-expo/commit/154afed)) 307 | * upgrade ts-jest to version 23.0.0 ([d2e8d94](https://github.com/bycedric/semantic-release-expo/commit/d2e8d94)) 308 | * use `chore` as commit type for new releases ([3cb214f](https://github.com/bycedric/semantic-release-expo/commit/3cb214f)) 309 | 310 | 311 | ### Documentation changes 312 | 313 | * remove excessive prepare step from example ([958b01c](https://github.com/bycedric/semantic-release-expo/commit/958b01c)) 314 | 315 | 316 | ### Other chores 317 | 318 | * add commitizen with peakfijn conventions ([28e5552](https://github.com/bycedric/semantic-release-expo/commit/28e5552)) 319 | * configure greenkeeper to use proper commit messages ([9986e1c](https://github.com/bycedric/semantic-release-expo/commit/9986e1c)) 320 | * rebuild changelog using peakfijn conventions ([3133aa5](https://github.com/bycedric/semantic-release-expo/commit/3133aa5)) 321 | * update semantic release config with the peakfijn conventions ([e7f80b9](https://github.com/bycedric/semantic-release-expo/commit/e7f80b9)) 322 | 323 | 324 | ### Pipeline changes 325 | 326 | * add commitlint with peakfijn conventions ([2e94a50](https://github.com/bycedric/semantic-release-expo/commit/2e94a50)) 327 | * use new single analyser format for releases ([7e46655](https://github.com/bycedric/semantic-release-expo/commit/7e46655)) 328 | 329 | # [1.1.0](https://github.com/bycedric/semantic-release-expo/compare/1.0.4...1.1.0) (2018-06-18) 330 | 331 | 332 | ### New features 333 | 334 | * add support for multiple manifests ([b8103d4](https://github.com/bycedric/semantic-release-expo/commit/b8103d4)), closes [#12](https://github.com/bycedric/semantic-release-expo/issues/12) 335 | * use git flow approach to releases ([4546556](https://github.com/bycedric/semantic-release-expo/commit/4546556)) 336 | 337 | 338 | ### Other chores 339 | 340 | * upgrade `jest` dependency to `jest@^23.1.0` ([08268ea](https://github.com/bycedric/semantic-release-expo/commit/08268ea)) 341 | 342 | 343 | 344 | ## [1.0.4](https://github.com/bycedric/semantic-release-expo/compare/1.0.3...1.0.4) (2018-06-17) 345 | 346 | 347 | ### Bug fixes 348 | 349 | * pin [@types](https://github.com/types)/fs-extra to 5.0.2 ([#10](https://github.com/bycedric/semantic-release-expo/issues/10)) ([6b34f57](https://github.com/bycedric/semantic-release-expo/commit/6b34f57)) 350 | 351 | 352 | ### Code refactors 353 | 354 | * remove unused `Config` type ([63fbb2c](https://github.com/bycedric/semantic-release-expo/commit/63fbb2c)) 355 | 356 | 357 | ### Code style changes 358 | 359 | * reindent package file with tabs ([f1e4333](https://github.com/bycedric/semantic-release-expo/commit/f1e4333)) 360 | 361 | 362 | ### Other chores 363 | 364 | * add terminal cast as example ([788092e](https://github.com/bycedric/semantic-release-expo/commit/788092e)) 365 | * update [@types](https://github.com/types)/jest to version 23.0.0 ([c00f165](https://github.com/bycedric/semantic-release-expo/commit/c00f165)) 366 | 367 | 368 | 369 | ## [1.0.3](https://github.com/bycedric/semantic-release-expo/compare/1.0.2...1.0.3) (2018-05-20) 370 | 371 | 372 | ### Bug fixes 373 | 374 | * detect indentation and new lines to keep style intact ([94379c9](https://github.com/bycedric/semantic-release-expo/commit/94379c9)) 375 | 376 | 377 | ### Testing updates 378 | 379 | * add extra validation for new line detection ([60b4a67](https://github.com/bycedric/semantic-release-expo/commit/60b4a67)) 380 | 381 | 382 | 383 | ## [1.0.2](https://github.com/bycedric/semantic-release-expo/compare/1.0.1...1.0.2) (2018-05-20) 384 | 385 | 386 | ### Bug fixes 387 | 388 | * add changelog plugin for semantic releases ([586047e](https://github.com/bycedric/semantic-release-expo/commit/586047e)) 389 | 390 | 391 | 392 | ## [1.0.1](https://github.com/bycedric/semantic-release-expo/compare/1.0.0...1.0.1) (2018-05-20) 393 | 394 | 395 | ### Bug fixes 396 | 397 | * remove chore from triggering new version ([06b8a5e](https://github.com/bycedric/semantic-release-expo/commit/06b8a5e)) 398 | 399 | 400 | 401 | # [1.0.0](https://github.com/bycedric/semantic-release-expo/compare/63891a6...1.0.0) (2018-05-20) 402 | 403 | 404 | ### Bug fixes 405 | 406 | * add semantic release and proper branch name ([eb3aa6c](https://github.com/bycedric/semantic-release-expo/commit/eb3aa6c)) 407 | * use proper casing in badges in readme ([1adea31](https://github.com/bycedric/semantic-release-expo/commit/1adea31)) 408 | 409 | 410 | ### New features 411 | 412 | * add first draft of expo releases ([67c0f6f](https://github.com/bycedric/semantic-release-expo/commit/67c0f6f)) 413 | * integrate semantic release in travis ([cbe0582](https://github.com/bycedric/semantic-release-expo/commit/cbe0582)) 414 | 415 | 416 | ### Other chores 417 | 418 | * initial project structure setup ([63891a6](https://github.com/bycedric/semantic-release-expo/commit/63891a6)) 419 | -------------------------------------------------------------------------------- /docs/terminal.svg: -------------------------------------------------------------------------------- 1 | cedricatCedrics-MacBook-Proin~/Projects/cool-stuff/expoondevelop>>npx>npxsemantic-release[Semanticrelease]:Runningsemantic-releaseversion15.5.2[Semanticrelease]:Loadplugin"verifyConditions"from@semantic-release/changelog[Semanticrelease]:Loadplugin"verifyConditions"fromsemantic-release-expo[Semanticrelease]:Loadplugin"verifyConditions"fromsemantic-release-git-branches[Semanticrelease]:Loadplugin"analyzeCommits"from@semantic-release/commit-analyzer[Semanticrelease]:Loadplugin"generateNotes"from@semantic-release/release-notes-generator[Semanticrelease]:Loadplugin"prepare"from@semantic-release/changelog[Semanticrelease]:Loadplugin"prepare"fromsemantic-release-expo[Semanticrelease]:Loadplugin"prepare"fromsemantic-release-git-branches[Semanticrelease]:Loadplugin"publish"from@semantic-release/exec[Semanticrelease]:Runautomatedreleasefrombranchdevelop[Semanticrelease]:Callpluginverify-conditions[Semanticrelease]:FoundExpomanifestforCoolStuff[Semanticrelease]:Foundgittag0.4.0associatedwithversion0.4.0[Semanticrelease]:Found1commitssincelastrelease[Semanticrelease]:Callpluginanalyze-commits[Semanticrelease]:Analyzingcommit:feature:mycoolnewfeature[Semanticrelease]:Thereleasetypeforthecommitisminor[Semanticrelease]:Analysisof1commitscomplete:minorrelease[Semanticrelease]:Thenextreleaseversionis0.5.0[Semanticrelease]:Callpluginverify-release[Semanticrelease]:CallplugingenerateNotes[Semanticrelease]:Callpluginprepare[Semanticrelease]:UpdateCHANGELOG.md[Semanticrelease]:Expomanifestversionchanged(0.4.0=>0.5.0)[Semanticrelease]:Expomanifestandroidversionchanged(9=>10)[Semanticrelease]:Expomanifestiosversionchanged(0.4.0=>0.5.0)[Semanticrelease]:NewExpomanifestwritten[Semanticrelease]:Creatingnewreleasebranchrelease/0.5.0[Semanticrelease]:AddCHANGELOG.mdtothereleasecommit[Semanticrelease]:Addapp.jsontothereleasecommit[Semanticrelease]:Found2file(s)tocommit[Semanticrelease]:Creatingtag0.5.0[Semanticrelease]:Pushingreleasebranchrelease/0.5.0toremote[Semanticrelease]:Pullingbranchdevelop[Semanticrelease]:Mergingreleasebranchrelease/0.5.0intodevelop[Semanticrelease]:Pushingupdatedbranchdevelop[Semanticrelease]:Cleaningupfornextbranch...[Semanticrelease]:Pullingbranchmaster[Semanticrelease]:Mergingreleasebranchrelease/0.5.0intomaster[Semanticrelease]:Pushingupdatedbranchmaster[Semanticrelease]:PreparedGitrelease:0.5.0[Semanticrelease]:Createtag0.5.0[Semanticrelease]:Callpluginpublish[Semanticrelease]:Callscriptecho"done!"done![Semanticrelease]:Thecommandecho"done!"wroteinvalidJSONtostdout.Thestdoutcontentwillbeignored.[Semanticrelease]:Publishedrelease:0.5.0cedricatCedrics-MacBook-Proin~/Projects/cool-stuff/expoonrelease/0.5.0>n>np>npxs>npxse>npxsem>npxsema>npxseman>npxsemant>npxsemanti>npxsemantic>npxsemantic->npxsemantic-r>npxsemantic-re>npxsemantic-rel>npxsemantic-rele>npxsemantic-relea>npxsemantic-releas --------------------------------------------------------------------------------