Skip to content

Commit

Permalink
feat: add monorepo support (#232)
Browse files Browse the repository at this point in the history
* chore: remove filesToBump

* feat: update versions on each package

* feat: run publish command on each package

* fix: fix broken test

* feat: add readVersionFrom to monorepo config

* docs: update guide
  • Loading branch information
Eunjae Lee authored Sep 6, 2019
1 parent acd0f13 commit aa96ca9
Show file tree
Hide file tree
Showing 30 changed files with 230 additions and 114 deletions.
31 changes: 16 additions & 15 deletions GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<!-- toc -->

- [Installation](#installation)
- [Install `hub`](#install-hub)
- [Dry Mode](#dry-mode)
- [Integrate with Circle CI](#integrate-with-circle-ci)
- [NPM Token](#npm-token)
Expand Down Expand Up @@ -212,27 +213,27 @@ By default, `publishCommand` returns `yarn publish` or `npm publish`. You can ov

### Release your monorepo project

Ship.js currently supports monorepo project unless you want independent versioning in your packages.

Let's say you have the following package.json files:

- package.json
- packages/first-package/package.json
- packages/second-package/package.json
- example/package.json
Ship.js currently supports monorepo project(Independent versioning is not supported at the moment).

```js
module.exports = {
filesToBump: [
"package.json",
"packages/first-package/package.json",
"packages/second-package/package.json",
"example/package.json"
]
monorepo: {
readVersionFrom: 'package.json',
packagesToBump: ['packages/*', 'examples/*'],
packagesToPublish: ['packages/*'],
}
};
```

With the config above, Ship.js will read the current version from the first entry from the array, which is `package.json`. After figuring out the next version, the next version will be updated to the all package.json files.
With the config above, `prepare` command will

1. Read the current version from `package.json` file at the project root directory.
2. Calculate the next version based on commit messages.
3. Update the next version over `package.json` files in `['packages/*', 'examples/*']`.

And `release` command will publish packages in `['packages/*']`.

When Ship.js handles `packagesToBump` and `packagesToPublish`, it will only list directories with `package.json` inside them.

### Schedule your release

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"tw2": "yarn workspace shipjs test:watch",
"bootstrap": "./packages/shipjs-lib/tests/bootstrap.sh",
"release:prepare": "npx shipjs prepare",
"release:trigger": "npx shipjs release"
"release:trigger": "npx shipjs release",
"toc": "npx markdown-toc -i --bullets=\"-\" GUIDE.md"
},
"author": "Algolia <[email protected]>",
"license": "MIT",
Expand Down
5 changes: 2 additions & 3 deletions packages/shipjs-lib/src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
/* version */
export { default as getCurrentVersion } from './lib/util/getCurrentVersion';
export {
default as hasTagForCurrentVersion,
} from './lib/util/hasTagForCurrentVersion';
export { default as getNextVersion } from './lib/util/getNextVersion';
export { default as updateVersion } from './lib/util/updateVersion';
export { default as isValidVersion } from './lib/util/isValidVersion';
Expand All @@ -19,6 +16,7 @@ export { default as getRepoURL } from './lib/git/getRepoURL';
export { default as getLatestCommitHash } from './lib/git/getLatestCommitHash';
export { default as getCommitUrl } from './lib/git/getCommitUrl';
export { default as isWorkingTreeClean } from './lib/git/isWorkingTreeClean';
export { default as hasTag } from './lib/git/hasTag';

/* shell */
export { default as exec } from './lib/shell/exec';
Expand All @@ -29,3 +27,4 @@ export { default as loadConfig } from './lib/config/loadConfig';

/* etc */
export { default as getAppName } from './lib/util/getAppName';
export { default as expandPackageList } from './lib/util/expandPackageList';
10 changes: 7 additions & 3 deletions packages/shipjs-lib/src/lib/config/defaultConfig.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export default {
remote: 'origin',
filesToBump: ['package.json'],
// monorepo: {
// readVersionFrom: 'package.json',
// packagesToBump: ['packages/*', 'examples/*'],
// packagesToPublish: ['packages/*'],
// },
updateChangelog: true,
conventionalChangelogArgs: '-p angular -i CHANGELOG.md -s',
installCommand: ({ isYarn }) => (isYarn ? 'yarn install' : 'npm install'),
Expand Down Expand Up @@ -70,8 +74,8 @@ export default {
].join(', ')}]`;
},
buildCommand: ({ isYarn }) => (isYarn ? 'yarn build' : 'npm run build'),
publishCommand: ({ isYarn, defaultCommand }) => defaultCommand,
getTagName: ({ currentVersion }) => `v${currentVersion}`,
publishCommand: ({ isYarn, tag, defaultCommand, dir }) => defaultCommand,
getTagName: ({ version }) => `v${version}`,
testCommandBeforeRelease: ({ isYarn }) =>
isYarn ? 'yarn test' : 'npm run test',
appName: undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import silentExec from '../../shell/silentExec';
import expandPackageList from '../expandPackageList';

describe('expandPackageList', () => {
it('expands package list', () => {
silentExec('./tests/bootstrap-examples/simple-monorepo.sh');
expect(expandPackageList(['.'], 'sandbox/simple-monorepo')).toEqual([
`${process.cwd()}/sandbox/simple-monorepo`,
]);

expect(
expandPackageList(['.', 'packages/*'], 'sandbox/simple-monorepo')
).toEqual([
`${process.cwd()}/sandbox/simple-monorepo`,
`${process.cwd()}/sandbox/simple-monorepo/packages/package_a`,
`${process.cwd()}/sandbox/simple-monorepo/packages/package_b`,
]);
});

it('gets only directories with package.json', () => {
silentExec('./tests/bootstrap-examples/monorepo-with-nonpkg-directory.sh');
const projectName = 'monorepo-with-nonpkg-directory';
expect(
expandPackageList(['.', 'packages/*'], `sandbox/${projectName}`)
).toEqual([
`${process.cwd()}/sandbox/${projectName}`,
`${process.cwd()}/sandbox/${projectName}/packages/package_a`,
`${process.cwd()}/sandbox/${projectName}/packages/package_b`,
]);
});
});

This file was deleted.

25 changes: 20 additions & 5 deletions packages/shipjs-lib/src/lib/util/__tests__/getNextVersion.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,31 +148,46 @@ describe('getNextVersionFromCommitMessages', () => {
describe('getNextVersion', () => {
it('gets next version with patch updated', () => {
silentExec('./tests/bootstrap-examples/patch-version-up.sh');
const { version: actual } = getNextVersion('sandbox/patch-version-up');
const { version: actual } = getNextVersion(
'0.0.1',
'sandbox/patch-version-up'
);
expect(actual).toBe('0.0.2');
});

it('gets next version with minor updated', () => {
silentExec('./tests/bootstrap-examples/minor-version-up.sh');
const { version: actual } = getNextVersion('sandbox/minor-version-up');
const { version: actual } = getNextVersion(
'0.0.1',
'sandbox/minor-version-up'
);
expect(actual).toBe('0.1.0');
});

it('gets next version with major updated', () => {
silentExec('./tests/bootstrap-examples/major-version-up.sh');
const { version: actual } = getNextVersion('sandbox/major-version-up');
const { version: actual } = getNextVersion(
'0.0.1',
'sandbox/major-version-up'
);
expect(actual).toBe('1.0.0');
});

it('gets a null with no commit messages', () => {
silentExec('./tests/bootstrap-examples/empty.sh no-commit-log');
const { version: actual } = getNextVersion('sandbox/no-commit-log');
const { version: actual } = getNextVersion(
'0.0.1',
'sandbox/no-commit-log'
);
expect(actual).toBe(null);
});

it('throws when there is a commit message out of convention', () => {
silentExec('./tests/bootstrap-examples/out-of-convention.sh');
const { ignoredMessages } = getNextVersion('sandbox/out-of-convention');
const { ignoredMessages } = getNextVersion(
'0.0.1',
'sandbox/out-of-convention'
);
expect(ignoredMessages).toEqual(['hello: add a']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { resolve } from 'path';
describe('updateVersion', () => {
it('update version correctly', () => {
silentExec('./tests/bootstrap-examples/empty.sh version-updating');
const filePath = resolve('sandbox/version-updating', 'package.json');
updateVersion([filePath], '0.9.9');
const dir = resolve('sandbox/version-updating');
updateVersion('0.9.9', dir);
expect(getCurrentVersion('sandbox/version-updating')).toBe('0.9.9');
});
});
23 changes: 23 additions & 0 deletions packages/shipjs-lib/src/lib/util/expandPackageList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolve, join } from 'path';
import { statSync, readdirSync, existsSync } from 'fs';

const isDirectory = dir => statSync(dir).isDirectory();
const getDirectories = dir =>
readdirSync(dir)
.map(name => join(dir, name))
.filter(isDirectory);
const hasPackageJson = dir => existsSync(`${dir}/package.json`);
const flatten = arr => arr.reduce((acc, item) => acc.concat(item), []);

export default function expandPackageList(list, dir = '.') {
return flatten(
list.map(item => {
if (item.endsWith('/*')) {
const basePath = resolve(dir, item.slice(0, item.length - 2));
return getDirectories(basePath).filter(hasPackageJson);
} else {
return resolve(dir, item);
}
})
);
}
4 changes: 2 additions & 2 deletions packages/shipjs-lib/src/lib/util/getAppName.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { resolve } from 'path';
import loadConfig from '../config/loadConfig';

export default function getAppName(dir = '.') {
const { appName, filesToBump } = loadConfig(dir);
const { appName } = loadConfig(dir);
if (appName) {
return appName;
}
const { name } = JSON.parse(readFileSync(resolve(dir, filesToBump[0])));
const { name } = JSON.parse(readFileSync(resolve(dir, 'package.json')));
return name;
}
9 changes: 5 additions & 4 deletions packages/shipjs-lib/src/lib/util/getCurrentVersion.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { readFileSync } from 'fs';
import { resolve } from 'path';
import loadConfig from '../config/loadConfig';

export default function getCurrentVersion(dir = '.') {
const { filesToBump } = loadConfig(dir);
const { version } = JSON.parse(readFileSync(resolve(dir, filesToBump[0])));
export default function getCurrentVersion(
dir = '.',
filename = 'package.json'
) {
const { version } = JSON.parse(readFileSync(resolve(dir, filename)));
return version;
}
10 changes: 4 additions & 6 deletions packages/shipjs-lib/src/lib/util/getNextVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
GIT_COMMIT_BREAKING_CHANGE,
} from '../const';
import { inc, prerelease } from 'semver';
import getCurrentVersion from './getCurrentVersion';
import silentExec from '../shell/silentExec';

export function getNextVersionFromCommitMessages(version, titles, bodies) {
Expand Down Expand Up @@ -63,9 +62,8 @@ function getBodies(version, dir) {
.trim();
}

export default function getNextVersion(dir = '.') {
const version = getCurrentVersion(dir);
const titles = getTitles(version, dir);
const bodies = getBodies(version, dir);
return getNextVersionFromCommitMessages(version, titles, bodies);
export default function getNextVersion(currentVersion, dir = '.') {
const titles = getTitles(currentVersion, dir);
const bodies = getBodies(currentVersion, dir);
return getNextVersionFromCommitMessages(currentVersion, titles, bodies);
}
7 changes: 0 additions & 7 deletions packages/shipjs-lib/src/lib/util/hasTagForCurrentVersion.js

This file was deleted.

12 changes: 5 additions & 7 deletions packages/shipjs-lib/src/lib/util/updateVersion.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';

export default function updateVersion(filesToBump, nextVersion, dir = '.') {
filesToBump.forEach(file => {
const filePath = resolve(dir, file);
const json = JSON.parse(readFileSync(filePath).toString());
json.version = nextVersion;
writeFileSync(filePath, `${JSON.stringify(json, null, 2)}\n`);
});
export default function updateVersion(nextVersion, dir = '.') {
const filePath = resolve(dir, 'package.json');
const json = JSON.parse(readFileSync(filePath).toString());
json.version = nextVersion;
writeFileSync(filePath, `${JSON.stringify(json, null, 2)}\n`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

./tests/bootstrap-examples/empty.sh monorepo-with-nonpkg-directory/ && \
./tests/bootstrap-examples/empty.sh monorepo-with-nonpkg-directory/packages/package_a && \
./tests/bootstrap-examples/empty.sh monorepo-with-nonpkg-directory/packages/package_b && \
mkdir sandbox/monorepo-with-nonpkg-directory/packages/not_a_package
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

./tests/bootstrap-examples/empty.sh simple-monorepo/ && \
./tests/bootstrap-examples/empty.sh simple-monorepo/packages/package_a && \
./tests/bootstrap-examples/empty.sh simple-monorepo/packages/package_b
10 changes: 7 additions & 3 deletions packages/shipjs/src/flow/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import getNextVersion from '../step/prepare/getNextVersion';
import confirmNextVersion from '../step/prepare/confirmNextVersion';
import prepareStagingBranch from '../step/prepare/prepareStagingBranch';
import checkoutToStagingBranch from '../step/prepare/checkoutToStagingBranch';
import updateVersions from '../step/prepare/updateVersions';
import updateVersion from '../step/prepare/updateVersion';
import updateVersionMonorepo from '../step/prepare/updateVersionMonorepo';
import installDependencies from '../step/prepare/installDependencies';
import updateChangelog from '../step/prepare/updateChangelog';
import commitChanges from '../step/prepare/commitChanges';
Expand Down Expand Up @@ -41,7 +42,7 @@ async function prepare({
validateMergeStrategy({ config });
pull({ dir, dryRun });
push({ config, currentBranch: baseBranch, dir, dryRun });
let { nextVersion } = getNextVersion({ dir });
let { nextVersion } = getNextVersion({ currentVersion, dir });
nextVersion = await confirmNextVersion({
yes,
currentVersion,
Expand All @@ -54,7 +55,10 @@ async function prepare({
dir,
});
checkoutToStagingBranch({ stagingBranch, dir, dryRun });
await updateVersions({ config, nextVersion, dir, dryRun });
const updateVersionFn = config.monorepo
? updateVersionMonorepo
: updateVersion;
await updateVersionFn({ config, nextVersion, dir, dryRun });
installDependencies({ config, dir, dryRun });
updateChangelog({ config, firstRelease, releaseCount, dir, dryRun });
await commitChanges({ nextVersion, dir, config, dryRun });
Expand Down
7 changes: 3 additions & 4 deletions packages/shipjs/src/flow/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ async function release({ help = false, dir = '.', dryRun = false }) {
printDryRunBanner();
}
const config = loadConfig(dir);
validate({ config, dir });
const { currentVersion: version } = validate({ config, dir });
const {
appName,
version,
latestCommitHash,
latestCommitUrl,
repoURL,
releaseTag,
} = gatherRepoInfo({ dir });
} = gatherRepoInfo({ version, dir });
await notifyReleaseStart({
config,
appName,
Expand All @@ -43,7 +42,7 @@ async function release({ help = false, dir = '.', dryRun = false }) {
runTest({ isYarn, config, dir, dryRun });
runBuild({ isYarn, config, dir, dryRun });
runPublish({ isYarn, config, releaseTag, dir, dryRun });
const { tagName } = createGitTag({ config, dir, dryRun });
const { tagName } = createGitTag({ version, config, dir, dryRun });
gitPush({ tagName, config, dir, dryRun });
await notifyReleaseSuccess({
config,
Expand Down
Loading

0 comments on commit aa96ca9

Please sign in to comment.