diff --git a/.travis.yml b/.travis.yml
index aa9c634..6dc5416 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,3 +4,4 @@ cache: yarn
script:
- yarn lint
- yarn test
+ - yarn test:e2e
diff --git a/jest.config-e2e.js b/jest.config-e2e.js
new file mode 100644
index 0000000..12e8eb1
--- /dev/null
+++ b/jest.config-e2e.js
@@ -0,0 +1,3 @@
+module.exports = {
+ testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(e2e).[jt]s?(x)"],
+}
diff --git a/package.json b/package.json
index 1be2ed9..4537819 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"homepage": "https://github.com/frinyvonnick/gitmoji-changelog#readme",
"scripts": {
"test": "jest",
+ "test:e2e": "jest --config ./jest.config-e2e.js",
"lint": "eslint packages/**/src"
},
"devDependencies": {
diff --git a/packages/gitmoji-changelog-cli/package.json b/packages/gitmoji-changelog-cli/package.json
index 5aca6af..3a0554e 100644
--- a/packages/gitmoji-changelog-cli/package.json
+++ b/packages/gitmoji-changelog-cli/package.json
@@ -37,6 +37,7 @@
"inquirer": "^6.3.1",
"libnpm": "^1.0.0",
"lodash": "^4.17.11",
+ "semver": "^5.6.0",
"semver-compare": "^1.0.0",
"simple-git": "^1.113.0",
"yargs": "^12.0.1"
diff --git a/packages/gitmoji-changelog-cli/src/cli.e2e.js b/packages/gitmoji-changelog-cli/src/cli.e2e.js
new file mode 100644
index 0000000..982df65
--- /dev/null
+++ b/packages/gitmoji-changelog-cli/src/cli.e2e.js
@@ -0,0 +1,343 @@
+const os = require('os')
+const fs = require('fs')
+const { removeSync } = require('fs-extra')
+const path = require('path')
+const simpleGit = require('simple-git/promise')
+const childProcess = require('child_process')
+
+expect.extend({
+ includes(str, items) {
+ const pass = items.every(item => str.includes(item))
+ return {
+ pass,
+ message: () => pass
+ ? `Expected ${str} to not includes ${items.join(', ')}`
+ : `Expected ${str} to includes ${items.join(', ')}`,
+ }
+ },
+})
+
+expect.extend({
+ toDisplayError(str) {
+ const pass = str.includes('Error')
+ return {
+ pass,
+ message: () => pass ? 'It passes' : `Expected ${str} to includes Error`,
+ }
+ },
+})
+
+describe('generate changelog', () => {
+ let testDir
+ let repo
+
+ beforeEach(async () => {
+ testDir = path.join(os.tmpdir(), 'gitmoji-changelog')
+ if (fs.existsSync(testDir)) removeSync(testDir)
+ fs.mkdirSync(testDir)
+ repo = simpleGit(testDir)
+ await repo.init()
+ fs.copyFileSync(
+ path.join(__dirname, '..', '..', '..', 'misc', 'package.sample.json'),
+ path.join(testDir, 'package.json'),
+ )
+ })
+
+ afterEach(() => {
+ removeSync(testDir)
+ })
+
+ describe('init', () => {
+ it("should get a 1.0.0 version while initializing changelog by calling cli without arguments and having package.json's version set to 1.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+
+ expect(getChangelog()).includes(['1.0.0'])
+ })
+
+ it("should get a 1.0.0 version while initializing changelog by calling cli with 1.0.0 and having package.json's version set to 0.0.1", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ gitmojiChangelog('1.0.0')
+ await commit(':bookmark: Version 1.0.0')
+
+ expect(getChangelog()).includes(['1.0.0'])
+ })
+
+ it("should get a next version while initializing changelog by calling cli without arguments and having package.json's version set to 1.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', 'next'])
+ })
+
+ it("should get two versions 1.0.0 and 2.0.0 while initializing changelog by calling cli without and having package.json's version set to 1.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ await bumpVersion('2.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 2.0.0')
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0'])
+ })
+ })
+
+ describe('update', () => {
+ it("should get two versions 1.0.0 and next while updating changelog by calling cli without arguments and having package.json's version set to 1.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', 'next'])
+ })
+ it("should get two versions 1.0.0 and 2.0.0 while updating changelog by calling cli without arguments and having package.json's version set to 2.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ await bumpVersion('2.0.0')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0'])
+ })
+ it("should get two versions 1.0.0 and 2.0.0 while updating changelog by calling cli with 2.0.0 and having package.json's version set to 1.0.0", async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog('2.0.0')
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0'])
+ })
+ it('should get three versions 1.0.0, 2.0.0, 3.0.0 while updating changelog by calling cli without arguments and skipping two tags creation 2.0.0 and 3.0.0', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ await bumpVersion('2.0.0')
+ await commit(':bookmark: Version 2.0.0')
+ await tag('2.0.0')
+
+ await makeChanges('file3')
+ await commit(':sparkles: Add a third file')
+ await bumpVersion('3.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 3.0.0')
+ await tag('3.0.0')
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0', '3.0.0'])
+ })
+
+ it('should get three versions 1.0.0, 2.0.0, 3.0.0 and next while updating changelog by calling cli without arguments and skipping two tags creation 2.0.0 and 3.0.0', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ await bumpVersion('2.0.0')
+ await commit(':bookmark: Version 2.0.0')
+ await tag('2.0.0')
+
+ await makeChanges('file3')
+ await commit(':sparkles: Add a third file')
+ await bumpVersion('3.0.0')
+ await commit(':bookmark: Version 3.0.0')
+ await tag('3.0.0')
+
+ await makeChanges('file4')
+ await commit(':sparkles: Add a fourth file')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0', '3.0.0', 'next'])
+ })
+
+ it('should get two versions 1.0.0, 2.0.0 and next while updating changelog by calling cli without arguments', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ await bumpVersion('2.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 2.0.0')
+ await tag('2.0.0')
+
+ await makeChanges('file4')
+ await commit(':sparkles: Add a fourth file')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', '2.0.0', 'next'])
+ })
+
+ it('shouldn\'t generate changelog when gimoji-changelog if there isn\'t any changes', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+ const output = gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0'])
+ expect(output.toString('utf8')).toDisplayError()
+ })
+
+ it('should get two versions 1.0.0 and next after two generation while updating changelog by calling cli without arguments', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog()
+
+ await makeChanges('file4')
+ await commit(':sparkles: Add a fourth file')
+ gitmojiChangelog()
+
+ expect(getChangelog()).includes(['1.0.0', 'next'])
+ })
+
+ it('should get two versions 1.0.0 and 1.1.0 after three generations while updating changelog by calling cli with version 1.1.0', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog()
+
+ await makeChanges('file4')
+ await commit(':sparkles: Add a fourth file')
+ gitmojiChangelog('1.1.0')
+
+ expect(getChangelog()).includes(['1.0.0', '1.1.0'])
+ })
+
+ it('should get two versions 1.0.0 and next after three generations while updating changelog by calling cli without arguments', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog('1.1.0')
+ gitmojiChangelog()
+
+ expect(getChangelog()).not.includes(['1.1.0'])
+ expect(getChangelog()).includes(['1.0.0', 'next'])
+ })
+
+ it('should get two versions 1.0.0 and 1.2.0 after three generations while updating changelog by calling cli without arguments', async () => {
+ await makeChanges('file1')
+ await commit(':sparkles: Add some file')
+ await bumpVersion('1.0.0')
+ gitmojiChangelog()
+ await commit(':bookmark: Version 1.0.0')
+ await tag('1.0.0')
+
+ await makeChanges('file2')
+ await commit(':sparkles: Add another file')
+ gitmojiChangelog('1.1.0')
+ gitmojiChangelog('1.2.0')
+
+ expect(getChangelog()).not.includes(['1.1.0'])
+ expect(getChangelog()).includes(['1.0.0', '1.2.0'])
+ })
+
+ it('should display an error if requested version isn\'t semver', async () => {
+ const output = gitmojiChangelog('awesomeversion')
+
+ expect(output.toString('utf8')).toDisplayError()
+ })
+ })
+
+ async function makeChanges(fileName) {
+ fs.writeFileSync(path.join(testDir, fileName))
+ }
+
+ async function commit(message) {
+ await repo.add('.')
+ await repo.commit(message)
+ }
+
+ async function tag(version) {
+ await repo.addTag(`v${version}`)
+ }
+
+ function gitmojiChangelog(args = []) {
+ if (!Array.isArray(args)) {
+ // eslint-disable-next-line no-param-reassign
+ args = [args]
+ }
+ return childProcess.execFileSync('node', [path.join(__dirname, 'index.js'), ...args], { cwd: testDir })
+ }
+
+ function getChangelog() {
+ return fs.readFileSync(path.join(testDir, 'CHANGELOG.md')).toString('utf8')
+ }
+
+ function bumpVersion(to) {
+ const pkg = path.join(testDir, 'package.json')
+ // eslint-disable-next-line global-require
+ const content = fs.readFileSync(pkg).toString('utf8')
+ const { version } = JSON.parse(content)
+ const updatedContent = content.replace(version, to)
+ fs.writeFileSync(pkg, updatedContent)
+ }
+})
diff --git a/packages/gitmoji-changelog-cli/src/cli.e2e.spec.js b/packages/gitmoji-changelog-cli/src/cli.e2e.spec.js
deleted file mode 100644
index 72e5559..0000000
--- a/packages/gitmoji-changelog-cli/src/cli.e2e.spec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-const os = require('os')
-const fs = require('fs')
-const { removeSync } = require('fs-extra')
-const path = require('path')
-const simpleGit = require('simple-git/promise')
-const util = require('util')
-const execFile = util.promisify(require('child_process').execFile)
-
-expect.extend({
- includes(str, items) {
- const pass = items.every(item => str.includes(item))
- return {
- pass,
- message: () => pass ? '' : '',
- }
- },
-})
-
-describe('generate changelog', () => {
- let testDir
- let repo
-
- beforeAll(async () => {
- testDir = path.join(os.tmpdir(), 'gitmoji-changelog')
- if (fs.existsSync(testDir)) removeSync(testDir)
- fs.mkdirSync(testDir)
- repo = simpleGit(testDir)
- await repo.init()
- fs.copyFileSync(
- path.join(__dirname, '..', '..', '..', 'misc', 'package.sample.json'),
- path.join(testDir, 'package.json'),
- )
- })
-
- afterAll(() => {
- removeSync(testDir)
- })
-
- it('should generate changelog even if user doesn\'t call cli at each tag', async () => {
- await newVersion('1.0.0')
- await gitmojiChangelog()
-
- expect(getChangelog()).toMatch(/1.0.0/)
-
- await newVersion('1.0.1')
- await newVersion('1.0.2')
- await gitmojiChangelog()
-
- expect(getChangelog()).includes(['1.0.0', '1.0.1', '1.0.2'])
- })
-
- async function newVersion(version) {
- fs.writeFileSync(path.join(testDir, version))
- bumpVersion(version)
- await repo.add('.')
- await repo.commit(`Commit ${version}`)
- await repo.addTag(`v${version}`)
- }
-
- function gitmojiChangelog() {
- return execFile('node', [path.join(__dirname, 'index.js')], { cwd: testDir })
- }
-
- function getChangelog() {
- return fs.readFileSync(path.join(testDir, 'CHANGELOG.md')).toString('utf8')
- }
-
- function bumpVersion(to) {
- const pkg = path.join(testDir, 'package.json')
- // eslint-disable-next-line global-require
- const { version } = require(pkg)
- const content = fs.readFileSync(pkg).toString('utf8')
- const updatedContent = content.replace(version, to)
- fs.writeFileSync(pkg, updatedContent)
- }
-})
diff --git a/packages/gitmoji-changelog-cli/src/cli.js b/packages/gitmoji-changelog-cli/src/cli.js
index 3973131..11ab8e7 100644
--- a/packages/gitmoji-changelog-cli/src/cli.js
+++ b/packages/gitmoji-changelog-cli/src/cli.js
@@ -1,11 +1,14 @@
const fs = require('fs')
const { set } = require('immutadot')
const libnpm = require('libnpm')
+const semver = require('semver')
const semverCompare = require('semver-compare')
const { generateChangelog, logger } = require('@gitmoji-changelog/core')
const { buildMarkdownFile, getLatestVersion } = require('@gitmoji-changelog/markdown')
const { executeInteractiveMode } = require('./interactiveMode')
+const { getPackageInfo, getRepoInfo } = require('./metaInfo')
+
const pkg = require('../package.json')
async function getGitmojiChangelogLatestVersion() {
@@ -47,7 +50,7 @@ async function main(options = {}) {
break
}
default: {
- const lastVersion = getLatestVersion(options.output)
+ const lastVersion = await getLatestVersion(options.output)
const newOptions = set(options, 'meta.lastVersion', lastVersion)
const changelog = await getChangelog(newOptions)
@@ -67,12 +70,29 @@ async function main(options = {}) {
}
async function getChangelog(options) {
- let changelog = await generateChangelog(options)
+ const packageInfo = await getPackageInfo()
+ const repository = await getRepoInfo(packageInfo)
+
+ const release = options.release === 'from-package' ? packageInfo.version : options.release
+
+ if (!semver.valid(release)) {
+ throw new Error(`${release} is not a valid semver version.`)
+ }
+
+ const enhancedOptions = {
+ ...options,
+ release,
+ }
+
+ let changelog = await generateChangelog(enhancedOptions)
if (options.interactive) {
changelog = await executeInteractiveMode(changelog)
}
+ changelog.meta.package = packageInfo
+ changelog.meta.repository = repository
+
return changelog
}
diff --git a/packages/gitmoji-changelog-core/src/metaInfo.js b/packages/gitmoji-changelog-cli/src/metaInfo.js
similarity index 100%
rename from packages/gitmoji-changelog-core/src/metaInfo.js
rename to packages/gitmoji-changelog-cli/src/metaInfo.js
diff --git a/packages/gitmoji-changelog-core/src/metaInfo.spec.js b/packages/gitmoji-changelog-cli/src/metaInfo.spec.js
similarity index 100%
rename from packages/gitmoji-changelog-core/src/metaInfo.spec.js
rename to packages/gitmoji-changelog-cli/src/metaInfo.spec.js
diff --git a/packages/gitmoji-changelog-core/src/index.js b/packages/gitmoji-changelog-core/src/index.js
index 789dbfc..229d594 100644
--- a/packages/gitmoji-changelog-core/src/index.js
+++ b/packages/gitmoji-changelog-core/src/index.js
@@ -4,11 +4,10 @@ const gitSemverTags = require('git-semver-tags')
const semverCompare = require('semver-compare')
const through = require('through2')
const concat = require('concat-stream')
-const { head, isEmpty, get } = require('lodash')
+const { isEmpty } = require('lodash')
const { promisify } = require('util')
const { parseCommit } = require('./parser')
-const { getPackageInfo, getRepoInfo } = require('./metaInfo')
const groupMapping = require('./groupMapping')
const logger = require('./logger')
const { groupSentencesByDistance } = require('./utils')
@@ -16,6 +15,8 @@ const { groupSentencesByDistance } = require('./utils')
const gitSemverTagsAsync = promisify(gitSemverTags)
const COMMIT_FORMAT = '%n%H%n%an%n%cI%n%s%n%b'
+const HEAD = ''
+const START = ''
function getCommits(from, to) {
return new Promise((resolve) => {
@@ -75,36 +76,51 @@ async function generateVersion(options) {
}))
}
- return {
+ const result = {
version,
- date: version !== 'next' ? getLastCommitDate(commits) : undefined,
groups: makeGroups(commits),
}
+
+ if (version !== 'next') {
+ result.date = getLastCommitDate(commits)
+ }
+
+ return result
}
-async function generateVersions({ tags, groupSimilarCommits, meta }) {
- let nextTag = ''
-
- const initialFrom = meta && meta.lastVersion ? meta.lastVersion : ''
- return Promise.all(
- [...tags, initialFrom]
- .map(tag => {
- const params = {
- groupSimilarCommits,
- from: tag,
- to: nextTag,
- version: nextTag ? sanitizeVersion(nextTag) : 'next',
- }
- nextTag = tag
- return params
- })
- .map(generateVersion)
- )
- .then(versions => versions.sort((c1, c2) => {
- if (c1.version === 'next') return -1
- if (c2.version === 'next') return 1
- return semverCompare(c2.version, c1.version)
- }))
+function sortVersions(c1, c2) {
+ if (c1.version === 'next') return -1
+ if (c2.version === 'next') return 1
+ return semverCompare(c2.version, c1.version)
+}
+
+function hasNextVersion(tags, release) {
+ if (!release || release === 'next') return true
+ return tags.some(tag => semver.eq(tag, release))
+}
+
+async function generateVersions({
+ tags,
+ hasNext,
+ release,
+ groupSimilarCommits,
+}) {
+ let nextTag = HEAD
+ const changes = await Promise.all(tags.map(async tag => {
+ let version = sanitizeVersion(nextTag)
+ if (!nextTag) {
+ version = hasNext ? 'next' : release
+ }
+ const from = tag
+ const to = nextTag
+ nextTag = tag
+ return generateVersion({
+ from, to, version, groupSimilarCommits,
+ })
+ }))
+ .then(versions => versions.sort(sortVersions))
+
+ return changes
}
async function generateChangelog(options = {}) {
@@ -112,57 +128,44 @@ async function generateChangelog(options = {}) {
mode,
release,
groupSimilarCommits,
- meta,
} = options
- const packageInfo = await getPackageInfo()
-
- let version = release === 'from-package' ? packageInfo.version : release
- if (version && version !== 'next') {
- if (!semver.valid(version)) {
- throw new Error(`${version} is not a valid semver version.`)
- }
-
- version = sanitizeVersion(version)
- }
-
- let changes = []
-
- const tags = await gitSemverTagsAsync()
- const lastTag = get(options, 'meta.lastVersion', head(tags))
+ const gitTags = await gitSemverTagsAsync()
+ let tagsToProcess = [...gitTags]
+ const hasNext = hasNextVersion(gitTags, release)
+ let lastVersion
if (mode === 'init') {
- changes = await generateVersions({ tags, groupSimilarCommits })
- } else if (lastTag === head(tags)) {
- const lastChanges = await generateVersion({
- groupSimilarCommits,
- from: lastTag,
- version,
- })
+ lastVersion = release
+ tagsToProcess = [...tagsToProcess, START]
+ } else {
+ const { meta } = options
+ lastVersion = meta && meta.lastVersion ? meta.lastVersion : undefined
- if (isEmpty(lastChanges.groups)) {
- throw new Error('No changes found. You may need to fetch or pull the last changes.')
+ if (lastVersion !== undefined) {
+ const lastVersionIndex = tagsToProcess.findIndex(tag => semver.eq(tag, lastVersion))
+ tagsToProcess.splice(lastVersionIndex + 1)
}
- changes.push(lastChanges)
- } else {
- const lastTagIndex = tags.findIndex(tag => tag === lastTag)
- const missingTags = tags.splice(0, lastTagIndex)
-
- const lastChanges = await generateVersions({ tags: missingTags, groupSimilarCommits, meta })
- changes = [
- ...changes,
- ...lastChanges,
- ]
+ if (hasNext && isEmpty(tagsToProcess)) {
+ tagsToProcess.push('')
+ }
}
- const repository = await getRepoInfo(packageInfo)
+ const changes = await generateVersions({
+ tags: tagsToProcess,
+ hasNext,
+ release,
+ groupSimilarCommits,
+ })
+
+ if (mode === 'update' && isEmpty(changes[0].groups)) {
+ throw new Error('No changes found. You may need to fetch or pull the last changes.')
+ }
return {
meta: {
- package: packageInfo,
- repository,
- lastVersion: sanitizeVersion(lastTag),
+ lastVersion: sanitizeVersion(lastVersion),
},
changes: changes.filter(({ groups }) => groups.length),
}
diff --git a/packages/gitmoji-changelog-core/src/index.spec.js b/packages/gitmoji-changelog-core/src/index.spec.js
index fd552dc..cd14bfb 100644
--- a/packages/gitmoji-changelog-core/src/index.spec.js
+++ b/packages/gitmoji-changelog-core/src/index.spec.js
@@ -81,8 +81,29 @@ const secondLipstickCommit = {
}
describe('changelog', () => {
+ it('should generate changelog for next release on init', async () => {
+ mockGroup([sparklesCommit])
+
+ gitSemverTags.mockImplementation(cb => cb(null, []))
+
+ const { changes } = await generateChangelog({ mode: 'init', release: 'next' })
+
+ expect(changes).toEqual([
+ {
+ version: 'next',
+ groups: [
+ {
+ group: 'added',
+ label: 'Added',
+ commits: expect.arrayContaining([expect.objectContaining(sparklesCommit)]),
+ },
+ ],
+ },
+ ])
+ })
+
it('should generate changelog for next release', async () => {
- mockGroups()
+ mockGroup([sparklesCommit])
gitSemverTags.mockImplementation(cb => cb(null, []))
@@ -103,7 +124,8 @@ describe('changelog', () => {
})
it('should generate changelog for all tags', async () => {
- mockGroups()
+ mockGroup([recycleCommit, secondRecycleCommit, lipstickCommit, secondLipstickCommit])
+ mockGroup([sparklesCommit])
gitSemverTags.mockImplementation(cb => cb(null, ['v1.0.0']))
@@ -117,10 +139,10 @@ describe('changelog', () => {
group: 'changed',
label: 'Changed',
commits: [
- secondRecycleCommit,
- secondLipstickCommit,
- lipstickCommit,
- recycleCommit,
+ expect.objectContaining({ subject: secondRecycleCommit.subject }),
+ expect.objectContaining({ subject: secondLipstickCommit.subject }),
+ expect.objectContaining({ subject: lipstickCommit.subject }),
+ expect.objectContaining({ subject: recycleCommit.subject }),
],
},
],
@@ -132,7 +154,35 @@ describe('changelog', () => {
{
group: 'added',
label: 'Added',
- commits: [sparklesCommit],
+ commits: [
+ expect.objectContaining({ subject: sparklesCommit.subject }),
+ ],
+ },
+ ],
+ },
+ ])
+ })
+
+ it('should generate a changelog with only next since lastVersion is provided to v1.0.0', async () => {
+ mockGroup([recycleCommit, secondRecycleCommit, lipstickCommit, secondLipstickCommit])
+
+ gitSemverTags.mockImplementation(cb => cb(null, ['v1.0.0']))
+
+ const { changes } = await generateChangelog({ mode: 'update', meta: { lastVersion: 'v1.0.0' } })
+
+ expect(changes).toEqual([
+ {
+ version: 'next',
+ groups: [
+ {
+ group: 'changed',
+ label: 'Changed',
+ commits: [
+ expect.objectContaining({ subject: secondRecycleCommit.subject }),
+ expect.objectContaining({ subject: secondLipstickCommit.subject }),
+ expect.objectContaining({ subject: lipstickCommit.subject }),
+ expect.objectContaining({ subject: recycleCommit.subject }),
+ ],
},
],
},
@@ -140,7 +190,8 @@ describe('changelog', () => {
})
it('should group similar commits', async () => {
- mockGroups()
+ mockGroup([])
+ mockGroup([recycleCommit, secondRecycleCommit, lipstickCommit, secondLipstickCommit])
gitSemverTags.mockImplementation(cb => cb(null, ['v1.0.0']))
@@ -190,8 +241,9 @@ describe('changelog', () => {
})
it('should get previous tag in from', async () => {
- mockGroups()
- mockGroup([sparklesCommit])
+ mockGroup([])
+ mockGroup([])
+ mockGroup([])
gitSemverTags.mockImplementation(cb => cb(null, ['v1.0.1', 'v1.0.0']))
@@ -281,8 +333,3 @@ function mockNoCommits() {
return readable
})
}
-
-function mockGroups() {
- mockGroup([sparklesCommit])
- mockGroup([recycleCommit, secondRecycleCommit, lipstickCommit, secondLipstickCommit])
-}
diff --git a/packages/gitmoji-changelog-markdown/package.json b/packages/gitmoji-changelog-markdown/package.json
index 8289414..1d40e52 100644
--- a/packages/gitmoji-changelog-markdown/package.json
+++ b/packages/gitmoji-changelog-markdown/package.json
@@ -22,7 +22,8 @@
"dependencies": {
"handlebars": "^4.0.14",
"immutadot": "^1.0.0",
- "lodash": "^4.17.11"
+ "lodash": "^4.17.11",
+ "semver": "^5.6.0"
},
"publishConfig": {
"access": "public",
diff --git a/packages/gitmoji-changelog-markdown/src/index.js b/packages/gitmoji-changelog-markdown/src/index.js
index bfbeb2d..e0c958b 100755
--- a/packages/gitmoji-changelog-markdown/src/index.js
+++ b/packages/gitmoji-changelog-markdown/src/index.js
@@ -1,3 +1,4 @@
+const semver = require('semver')
const { promisify } = require('util')
const fs = require('fs')
const path = require('path')
@@ -5,6 +6,9 @@ const { Transform } = require('stream')
const handlebars = require('handlebars')
const { update } = require('immutadot')
const { isEmpty } = require('lodash')
+const gitSemverTags = require('git-semver-tags')
+
+const gitSemverTagsAsync = promisify(gitSemverTags)
const MARKDOWN_TEMPLATE = path.join(__dirname, 'template.md')
@@ -103,7 +107,7 @@ function matchVersionBreakpoint(tested, version = '.*') {
return regex.test(tested)
}
-function getLatestVersion(markdownFile) {
+async function getLatestVersion(markdownFile) {
let markdownContent
try {
@@ -112,11 +116,19 @@ function getLatestVersion(markdownFile) {
return null
}
- const result = markdownContent.match(/<\/a>/)
+ const versions = markdownContent.match(/<\/a>/g)
+
+ if (!versions) return null
+
+ const [lastVersion, previousVersion] = versions
- if (!result) return null
+ const tags = await gitSemverTagsAsync()
+ const result = lastVersion.match(/<\/a>/)
+ const isNext = result[1] === 'next' || !tags.some(tag => semver.eq(tag, result[1]))
+ if (!isNext) return result[1]
- return `v${result[1]}`
+ const previousResult = previousVersion.match(/<\/a>/)
+ return previousResult[1]
}
function getShortHash(hash, repository) {